1

While refactoring I'm trying to retain some backwards compatibility for a time. I'm wondering if it's possible to have a method on an object, but prevent that method from being inherited by classes that subclass it? e.g. given

package Class {
    use Moose;

    sub foo { 'test' };
}

my $class = Class->new;
$class->foo;

would work, but

package Extended::Class {
    use Moose;
    extends 'Class';
}
my $class = Extended::Class->new;

$class->foo;

would not.

I realize this probably breaks some principle or another, but I'm deprecating these interfaces as I go.

xenoterracide
  • 16,274
  • 24
  • 118
  • 243
  • 1
    There's a [similar question for C#](http://stackoverflow.com/questions/727996/preventing-methods-from-being-inherited) and [one answer says that this would violate Liskov Substitution.](http://stackoverflow.com/a/727998/12570) The suggestion (for C#) is to override the `foo` method in the derived class and get it to throw an exception. – Peter K. May 08 '12 at 00:04
  • @PeterK. obviously that's an option, and I'm considering that, though I'm hoping someone has an idea about something I can do from within the class, instead of the subclasses. – xenoterracide May 08 '12 at 00:23
  • because an easy way to do this in the subclass is just to use [namespace::autoclean](https://metacpan.org/module/namespace::autoclean) `-also` – xenoterracide May 08 '12 at 00:33
  • 3
    Sounds like you're using inheritance when you shouldn't. Inheritance is used far too often. – ikegami May 08 '12 at 02:19
  • @ikegami repurposing the class to have a more sane inheritance – xenoterracide May 09 '12 at 00:02

3 Answers3

4

How about:

use 5.014;

package Class {
    use Carp qw( croak );
    use Moose;

    sub foo {
        my $self = shift;
        croak unless __PACKAGE__ eq ref $self;

        return 'test';
    }
}


package Extended::Class {
    use Moose;
    extends 'Class';
}


package main {
    my $x = Class->new;
    say $x->foo;

    my $y = Extended::Class->new;
    say $y->foo;
}
Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
1

Have you considered delegation?

package Original {
  use Moose;
  sub foo { 23 }
  sub bar { 42 }
}

package Subclass {
  use Moose;
  has original => (
    buidler => '_build_original',
    handles => [qw( bar )],
  );
  sub _build_original { Original->new }
}

Of course it depends on your situation if you can use it. The subclass won't pass isa checks for the above (but you can override isa if you must). Also passing the original arguments on to the object you're extending can be annoying depending on the use case.

phaylon
  • 1,914
  • 14
  • 13
0

Since it would look for the method foo in the Extended::Class first, you could just declare one there that doesn't do anything. That way the inherited one would not be called unless you do so somewhere in your subclass.

I'm not sure if Moose alters that behaviour, though.

package Class {
    use Moose;

    sub foo { 'test' }
}

package Extended::Class {
    use Moose;
    extends 'Class';

    sub foo {
        # do nothing
    }
}

package main {
    my $x = Class->new;
    my $y = Extended::Class->new;

    print $x->foo;
    print $y->foo;
}
simbabque
  • 53,749
  • 8
  • 73
  • 136