24

Here's a common scenario that's always frustrating for me to deal with.

I have an object model with a parent object. The parent contains some child objects. Something like this.

public class Zoo
{
    public List<Animal> Animals { get; set; }
    public bool IsDirty { get; set; }
}

Each child object has various data and methods

public class Animal
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void MakeMess()
    {
        ...
    }
}

When the child changes, in this case when the MakeMess method is called, some value in the parent needs to be updated. Let's say when a certain threshold of Animal's have made a mess, then the Zoo's IsDirty flag needs to be set.

There are a few ways to handle this scenario (that I know of).

1) Each Animal can have a parent Zoo reference in order to communicate changes.

public class Animal
{
    public Zoo Parent { get; set; }
    ...

    public void MakeMess()
    {
        Parent.OnAnimalMadeMess();
    }
}

This feels like the worst option since it couples Animal to its parent object. What if I want an animal who lives in a house?

2) Another option, if you're using a language that supports events (like C#) is to have the parent subscribe to change events.

public class Animal
{
    public event OnMakeMessDelegate OnMakeMess;

    public void MakeMess()
    {
        OnMakeMess();
    }
}

public class Zoo
{
    ...

    public void SubscribeToChanges()
    {
        foreach (var animal in Animals)
        {
            animal.OnMakeMess += new OnMakeMessDelegate(OnMakeMessHandler);
        }
    }

    public void OnMakeMessHandler(object sender, EventArgs e)
    {
        ...
    }
}

This seems to work but from experience gets hard to maintain. If Animals ever change Zoo's you have to unsubscribe events at the old Zoo and resubscribe at the new Zoo. This only gets worse as the composition tree gets deeper.

3) The other option is to move the logic up to the parent.

public class Zoo
{
    public void AnimalMakesMess(Animal animal)
    {
        ...
    }
}

This seems very unnatural and causes duplication of logic. For example, if I had a House object that doesn't share any common inheritance parent with Zoo..

public class House
{
    // Now I have to duplicate this logic
    public void AnimalMakesMess(Animal animal)
    {
        ...
    }
}

I have not yet found a good strategy for dealing with these situations. What else is available? How can this be made simpler?

2
  • You're right about #1 being bad, and I'm not keen on #2 either; generally you want to avoid side effects, and instead you're increasing them. Regarding option #3, why can't you factor out AnimalMakeMess into a static method that all classes can call?
    – Doval
    Commented Mar 22, 2014 at 0:06
  • 4
    #1 is not necessarly bad if you communicate through an interface (IAnimalObserver) instead of that specific Parent class.
    – coredump
    Commented Mar 22, 2014 at 14:09

7 Answers 7

12

I had to deal with this a couple times. The first time I used option 2 (events) and as you said it became really complicated. If you go that route, I highly suggest you need very thorough unit tests to make sure the events are done correctly and you're not leaving dangling references, otherwise it's a really big pain to debug.

The second time, I just implemented the parent property as a function of the children, so keep a Dirty property on each animal, and let Animal.IsDirty return this.Animals.Any(x => x.IsDirty). That was in the model. Above the model there was a Controller, and the controller's job was to know that after I changed the model (all actions on the model were passed through the controller so it knew that something had changed), then it knew it had to call certain re-evaluation functions, like triggering the ZooMaintenance department to check if the Zoo was dirty again. Alternatively I could just push the ZooMaintenance checks off until some scheduled later time (every 100 ms, 1 second, 2 minutes, 24 hours, whatever was necessary).

I found the latter has been much simpler to maintain, and my fears of performance problems never materialized.

Edit

Another way of dealing with this is a Message Bus pattern. Rather than using a Controller like in my example, you inject every object with an IMessageBus service. The Animal class can then publish a message, like "Mess Made" and your Zoo class can subscribe to the "Mess Made" message. The message bus service will take care of notifying the Zoo when any animal publishes one of those messages, and it can re-evaluate its IsDirty property.

This has the advantage that Animals no longer need a Zoo reference, and the Zoo doesn't need to worry about subscribing and unsubscribing from events from every single Animal. The penalty is that all Zoo classes subscribing to that message will have to re-evaluate their properties, even if it wasn't one of its animals. That may or may not be a big deal. If there's only one or two Zoo instances, it's probably fine.

Edit 2

Don't discount the simplicity of option 1. Anyone revisiting the code won't have much problem understanding it. It'll be obvious to someone looking at the Animal class that when MakeMess is called that it propagates the message up to the Zoo and it'll be obvious to the Zoo class where it gets its messages from. Remember that in object-oriented programming, a method call used to be called a "message". In fact, the only time it makes much sense to break from option 1 is if more than just the Zoo has to be notified if the Animal makes a mess. If there were more objects that needed to be notified, then I would probably move to a message bus or a controller.

7

I've made a simple class diagram that describes your domain: enter image description here

Each Animal has a Habitat it messes up.

The Habitat does not care what or how many animals it has (unless it is fundamentally part of your design which in this case you described it is not).

But the Animal does care, because it will behave differently in every Habitat.

This diagram is similar to the UML Diagram of the strategy design pattern, but we will use it differently.

Here are some code examples in Java (I don't want to make any C# specific mistakes).

Of course you can make your own tweak to this design, language and requirements.

This is the Strategy interface:

public interface Habitat {
    public void messUp(float magnitude);

    public float getCleanliness();
}

An example of a concrete Habitat. of course each Habitat subclass can implement these methods differently.

public class Zoo implements Habitat {
    public float cleanliness = 1;

    public float getCleanliness() {
        return cleanliness;
    }

    public void messUp(float magnitude) {
        cleanliness -= magnitude;
    }
}

Of course you can have multiple animal subclasses, where each one messes it up differently:

public class Animel {
    private Habitat habitat;

    public void makeMess() {
        habitat.messUp(.05f);
    }

    public Animel addTo(Habitat habitat) {
        this.habitat = habitat;
        return this;
    }
}

This is the client class, this basically explains how you can use this design.

public class ZooKeeper {
    public Habitat zoo = new Zoo();

    public ZooKeeper() {
        new Animal()
            .addTo( zoo )
            .makeMess();

        if (zoo.getCleanliness() < 0.5f) {
            System.out.println("The zoo is really messy");
        } else {
            System.out.println("The zoo looks clean");
        }
    }
}

Of course in your real application you can let the Habitat know and manage the Animal if you need.

3

I've had a fair amount of success with architectures like your option 2 in the past. It's the most general option and will allow the greatest flexibility. But, if you have control over your listeners and aren't managing a lot of subscription types, you can subscribing to events more easily by creating an interface.

interface MessablePlace
{
  void OnMess(object sender, MessEvent e);
}

class MessEvent
{
  String DetailsOrWhatever;
}

The interface option has the advantage of being almost as simple as your option 1, but also allows you to house animals pretty effortlessly in a House or FairlyLand.

3
  • Option 1 is actually pretty straightforward. That's just a back-reference. But generalize it with interface called Dwelling and provide a MakeMess method on it. That breaks the circular dependency. Then when the animal makes a mess, it calls dwelling.MakeMess() too.

In the spirit of lex parsimoniae, I'm going to go with this one, although I'd probably use the chain solution below, knowing me. (This is just the same model that @Benjamin Albert suggests.)

Note that if you were modelling relational database tables, the relation would go the other way: Animal would have a reference to Zoo, and the collection of Animals for a Zoo would be a query result.

  • Taking that idea farther, you could use a chained architecture. That is, create an interface Messable, and in each messable item, include a reference to next. After creating a mess, call MakeMess on the next item.

So Zoo here is involved in making a mess, because it gets messy too. Have:

Zoo implements Messable
House implements Messable
Animal implements Messable
   Messable next

   MakeMess()
       messy = true
       next.MakeMess

So now you have a chain of things that get the message that a mess has been created.

  • Option 2, a publish/subscribe model could work here, but feels really heavyweight. The object and container have a known relationship, so it seems a little heavy-handed to use something more general than that.

  • Option 3: In this particular case, calling Zoo.MakeMess(animal) or House.MakeMess(animal) isn't actually a bad option, because a house may have different semantics for getting messy than a Zoo.

Even if you don't go down the chain route, it sounds like there are two issues here: 1) the issue is about propagating a change from an object to its container, 2) It sounds like you want to spin off an interface for the container to abstract where the animals can live.

...

If you have first-class functions, you can pass a function (or delegate) into Animal to call after it makes a mess. That's a little like the chain idea, except with a function instead of an interface.

public Animal
    Function afterMess

    public MakeMess()
        messy = true
        afterMess()

When the animal moves, just set a new delegate.

  • Taken to the extreme, you could use Aspect Oriented Programming (AOP) with "after" advice on MakeMess.
2

I would go with 1, but I would make the parent-child relationship along with notification logic into separate wrapper. This removes the dependency of Animal on Zoo and allows automatic management of parent-child relationships. But this requires you to remake the objects in the hierarchy into interfaces/abstract classes first and write a specific wrapper for each interface. But that could be removed using code generation.

Something like :

public interface IAnimal
{
    string Name { get; set; }
    int Age { get; set; }

    void MakeMess();
}

public class Animal : IAnimal
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void MakeMess()
    {
        // makes mess
    }
}

public class ZooAnimals
{
    class AnimalInZoo : IAnimal
    {
        public IAnimal _animal;
        public ZooAnimals _zoo;

        public AnimalInZoo(IAnimal animal, ZooAnimals zoo)
        {
            _animal = animal;
            _zoo = zoo;
        }

        public string Name { get { return _animal.Name; } set { _animal.Name = value; } }
        public int Age { get { return _animal.Age; } set { _animal.Age = value; } }

        public void MakeMess()
        {
            _animal.MakeMess();
            _zoo.IsDirty = true;
        }
    }

    private Collection<AnimalInZoo> animals = new Collection<AnimalInZoo>();

    public IAnimal Add(IAnimal animal)
    {
        if (animal is AnimalInZoo)
        {
            var inZoo = (AnimalInZoo)animal;
            if (inZoo._zoo != this)
            {
                // animal is in a different zoo, what to do ?
                // either move animal to this zoo
                // or throw an exception so caller is forced to remove the animal from previous zoo first
            }
        }

        var anim = new AnimalInZoo(animal, this);
        animals.Add(anim);
        return anim;
    }

    public IAnimal Remove(IAnimal animal)
    {
        if (!(animal is AnimalInZoo))
        {
            // animal is not in zoo, throw an exception?
        }
        var inZoo = (AnimalInZoo)animal;
        if (inZoo._zoo != this)
        {
            // animal is in a different zoo, throw an exception?
        }

        animals.Remove(inZoo);
        return inZoo._animal;
    }

    public bool IsDirty { get; set; }
}

This is actually how some ORMs do their change tracking on entities. They create wrappers around the entities and make you work with those. Those wrappers are usually made using reflection and dynamic code generation.

1

Two options I often use. You can use the second approach and put the logic for wiring up the event in the collection itself on the parent.

An alternate approach (which can actually be used with any of the three options) is to use containment. Make an AnimalContainer (or even make it a collection) that can live in the house or the zoo or anything else. It provides the tracking functionality associated with the animals, but avoids inheritance issues since it can be included in any object that needs it.

0

You start with a basic failure: child objects shouldn't know about their parents.

Do strings know that they're in a list? No. Do dates know they exist in a calendar? No.

The best option is to change your design so that this sort of scenario doesn't exist.

After than, consider inversion of control. Instead of MakeMess on Animal with a side effect or event, pass Zoo into the method. Option 1 is fine if you need to protect the invariant that Animal always needs to live somewhere. It's not a parent, but a peer association then.

Occasionally 2 and 3 will go okay, but the key architectural principle to follow is that children don't know about their parents.

2
  • I suspect this is more like a submit button in a form an than a string in a list.
    – svidgen
    Commented Mar 22, 2014 at 1:55
  • 1
    @svidgen - Then pass in a callback. More foolproof than an event, easier to reason about, and no naughty references to things it need not know.
    – Telastyn
    Commented Mar 22, 2014 at 2:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.