Partager via


Untying unnatural bonds

Didn't get to work on any of the Unit-testing/Collections work today. Instead I worked on redesigning a design pattern (which i have no name for) into something reasonable. It's hard to describe but it involves several objects (lets say 3 for example) all holding pointers to each other. However, depending on the state of the system only 1 or 2 of those objects are actually in a valid state (and only very rarely are all 3 valid). So not only do these 3 objects hold onto each other, but there's also a complicated communications protocol that informs each object when it is valid/invalid (yechh).

For example, say that object A holds onto B, but B holds onto some critical resource. Later we try to update A and so A has to tell B "let go of your resource/reinitialize yourself". If we screw up then we can end up with a deadlock (for example if A is waiting for C to get up to date, but C is locked by B). With the code in question i think it came out because of the following situation:

class C {

    void M1() {

        B b = GetB();

        // do stuff with b

    }

   

    void M2() {

        B b = GetB();

        //do stuff with b

    }

}

At some point we decided that getting B was expensive and we should cache that information:

class C {

    B b;

    void M1() {

        if (b == null) {

            b = GetB();

        }

        //do stuff with b

    }

    void M2() {

        if (b == null) {

            b = GetB();

        }

        //do stuff with b

    }

    void M3() {

        //uh oh, b is holding onto some things we need.

        b = null; //or b.Reinitialize();

        //do stuff without b

    }

}

Later we add functionality to C that fails because 'b' has locked some other resource. In order to support that we need to release 'b'.  Of course, it wasn't even this simple.  There was a complicated system of interdependencies between the state held in C (yechhh++).

Keeping track of which bit of state in C is valid or not just gets way too complicated and error-prone. I find this kind of model extremely confusing. Especially that part about an object reinitializing itself. I'm a big fan of objects that are, for all intents and purposes, immutable. i.e. you create it and for it's lifetime it's state is unchanged. It makes understanding the system much easier. If we release our B then we know we can easily get it back by reconstructing a new one from the same information that we constructed the old one with. For the above problem I ended up solving it by having A create B on demand and only hold onto it as long as necessary (i.e. moving back to the original model). This cleaned up things immensely because it meant there was less state in A for determining if B was valid or not, and it meant that anyone working in this class now didn't need to know all these complex rules for what operations were/weren't allowed.

Not sure if this is the best way to fix the problem, but it did fix it while cleaning up things, so I'm happy.

Comments