Slash Boxes
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
More | Login | Reply
Loading... please wait.
  • At a glance, if I was going to design the class structure you were looking for, I'd start with something like this:

    - Character
      - Race
        - Elf (@ISA InfraVision)
          - Drow
        - Human
        - Dwarf
      - Class
        - Thief (sub pick_pocket)
        - Magician

      - ElvenThief (@ISA Elf, Thief)
      - DwarfThief (@ISA Drawf, Thief)

    - Skills (contains defaults skill level settings)
      - Vision
        - InfraVision
       - Thef

    • With all due respect, I think you've inadvertently illustrated my point. Let's stick with Perl 5 and assume that delegation is the key to performing profession-specific behavior, but we won't use delegation yet. We'll assume four races, human, elf, dwarf and halfling. We'll also assume four professions, fighter, thief, magician and cleric. With the Character and Profession abstract base classes, we have a grand total of ten classes, each which encapsulates the basic attributes of what they're trying to model.

      Now how does an elf become a thief? For profession-based skills, the elf delegates the message to its profession slot. If it's trying to cast a spell, the thief's abstract base class has a method that returns a failure, saying the skill cannot be performed. If the elf tries to pick a lock, the profession slot already has a method that can attempt that. So far it's pretty simple, but delegation still takes a lot of code to pull off, but it's grunt work that is still conceptually clear.

      But what if we go with your idea of subclassing? We still have ten classes, but now we need to add additional subclasses for each profession/character combination. That's an additional 16 classes, plus the original 10, giving us a total of 26 classes. But wait! We forgot dual-classed characters. How do we handle a thief/magician? And there are actually triple classes characters allowed, so the number of classes starts to really explode. And then people want to add extra races and professions! Trying to manage and debug all of those classes becomes a nightmare, at least in large part because it's not appropriate to say that a thief isa elf or an elf isa thief (at least on the class level.) For a given instance it's appropriate to say that, but inheritance works on a class level, not an instance level.

      And with your idea of an ElfThief, what methods and attributes should be in that class? If I think nothing should be in there and I'm just using this to combine all sorts of behaviors, then I subvert the entire idea of OO. A subclass is to provide a more specific implementation of something, not to just arbitrarily combine sets of behavior. But it turns out I can't use an empty class when I have an ElvenMagicianCleric because I have to disambiguate who gets to cast a spell, which means I do need code for this, so I am back to the problem of managing code for too many classes. And it's likely the disambiguation code for this class also applies to the (?:Human|Dwarf|Halfling)MagicianCleric (assuming they were all valid combinations) so I'm back to duplicating code.

      So how do we handle multiple classed characters with delegation? As a naive, but simple implementation, we can do this:

      sub Character::perform {
          my ($self, $action, @arguments) = @_;
          # the following prevents a poor code from arbitrarily calling any method
          my $method = $self->{actions}{$action} or die "No such action ($action)";
          foreach my $profession ($self->profession) {
              my $result;
              eval { $result = $profession->$method(@arguments) };
              return $result if $result;
          # if we got to hear, they couldn't perform the action
          warn "Attempting to perform ($action) failed";

      Note that this goes in the abstract base class, so everyone has this behavior available. Admittedly, this is a first pass and definitely inadequate, but it handles one or more professions per character and does not require a huge number of classes. Adding a new profession means creating one and only one class. There are still ordering problems, but we have these with any type of program and if they're solved for this issue, they can be solve for a much smaller set of classes.

      • 1) I agree that a class structure for each class / race would get way out of hand. I fail to understand why you need a class structure to handle the problem in the first place. It seems to me that every character could attempt any behaviour, so you could just have a default set of skill levels, and perform procedural adjustment to those stats based on classes/races. I have no idea what methods / attributes would useful in individual class / race classes.

        2) If the class structure *is* needed for some reaso

        • Those are valid questions. The truth is, you never need objects to accomplish any programming problem. I chose objects for this illustration primarily because characters are really bundles of data with behaviors attached to them. This, of course, is one way objects are defined.

          As for how mixins solve the problem more elegantly, you might want to read a virtually identical node on Perlmonks []. In light of the responses, I back off a bit from asserting the utility of mixins once it became clear that they'