samedi 30 janvier 2016

What design patterns can help model objects whose behaviors change dynamically, or objects with many optional behaviors?

Sorry for the long-winded examples, but I've run into this type of software design problem recently, and have been thinking about it. Not sure if there's a term for it, so I'll give 2 general examples to give an idea:

Ex 1: You're working on an RPG game, and you have a class for the main character. That character's reactions to the game world changes based on what you're wearing/holding, skills allotted, basically your object's internal state.

Say you have the following items in the game:

  • Ring of regeneration: allows your character to regenerate health over time
  • Sneaky sneakers: increases sneak
  • Magic mirror: reflects % of incoming damage
  • Doofy glasses: highlights crafting resources

Ex 2: You have a Toyota Corolla. There are different configurations of it, but they're all Toyota Corollas. ABS, and Traction Control are optional features (behaviors) you can add to the baseline model.

  • The baseline model does nothing extra when running.
  • With ABS, the car checks and responds for sudden stops when the car's running
  • With Traction Control, the car checks and responds to loss of traction when the car's running
  • And obviously, when you have a car with both, you'll do both behaviors when the car's running.

Common properties of the two examples:

  • the class of importance has a concrete blank slate that it can start with
  • optional items/parts can add an ability or extra behavior to that object; something extra to do per game tick/while running
  • may or may not mutate the object when item/behavior is added or taken off of it (increment sneak when putting on sneakers, decrement it when taking it off)

Potential solutions (but not adequate):

if statements:

if ch.isWearing 'doofy glasses':
    render with doofy shader
else if ch.isWearing ...

Doesn't work. Need to add a clause for every part. Class can get big and complicated very fast.

Strategy pattern:

class TractionControlStrategy
class ABSstrategy

class Toyota:
    TractionControlStrategy tcs
    ABSstrategy abs
    run():
        if tcs not null:
            tcs.run()
        if abs not null:
            abs.run()

carWithTCS = new Toyota(new TractionControlStrategy())

Not much better than the previous solution as you still have the long list of if statements

Strategy with subclasses:

class Toyota:
    run():
        // does nothing

class ToyotaWithABS : Toyota
    ABSstrategy abs = new ABSstrategy()
    run():
        abs.run

class ToyotaWithTCS : Toyota ...

Satisfies the Open/Closed Principle I think. Better than the previous one maybe? But now you'll have to create a class for every combination of configurations. If you found out later on that there's other optional features, then the number of classes would double for every feature you need to implement...

How can these types of interactions and behaviors be modeled with OOP? What design patterns, or combinations of design patterns promote this kind of thing?

Really not sure if this is a good question or if I'm clear with what I'm asking as I've never really practiced good software design.

I'm learning OpenGL, working on my 3D mesh/model class. This question is related because in my renderer, indexing and textures are optional for a mesh. So a mesh can be vertices only, indexed vertices, vertices and textures, or all 3. Plus, I can't foresee what features I may want to add in the future as I don't know what I'll be learning like a month down the line, so I need the class to be flexible, and extensible

Aucun commentaire:

Enregistrer un commentaire