Pattern - Pour water from the kettle

    how does a programmer boil water in a teapot?
    1. Gets water in the kettle
    2. Put the kettle on fire
    3. Waits until he boils
    how does a programmer boil water in a kettle if it already has water in it?
    1. Pours water from the kettle, which reduces the problem to the already solved
    (old joke)


    For each task there are a bunch of different solutions. Smart, stupid, fast, cunning. In real life, decisions are divided into two types - those that work and everyone else.

    Imagine that you have complex code (your own or someone else's), and you need to seriously influence the logic of its work? Of course, you can go deeper, explore dependencies, and think about the possible consequences and problems. Even for your code, this is not easy. For a stranger, complexity is just wild.

    When you are faced with a similar task - it's time to remember the pattern "pour water from the kettle" (it sounds almost like a Chinese strategy =))
    And instead of doing a difficult task - to reduce it to a solution to an existing one. Thus, you do not modify complex, and potentially dangerous code, but write your own, small piece of code, the correctness of which is easy to verify.

    examples
    Once upon a time, our company created a database for Lotus Notes. But alas, it was not pleasant for customers to press Tab to go to another cell of the table - since they were used to Excel, where you had to press Enter to go. We did not find a single standard tool for replacing the Tab key with Enter, and therefore resorted to the following solution. We hung a keyboard hook that replaced the Enter key on Tab if the key click occurred in our application tables.

    another example is jabber transports
    jabber client does not need to know about the existence of other protocols. It works only on xmpp. The jabber server is engaged in the translation.

    Well, an example from the code
    class int
    {
    public:
        Int (const int iv)
        : i (iv)
        {}
        Int (const Int & copy)
        {
            * this = copy;
        }
        // copy and assignment constructors are merged here.
        // So if you add a new variable to the class -
        // you will need to add it only to one place in the code, and not to several
        Int & operator = (const Int & copy)
        {
            i = copy.i;
            return * this;
        }
        Int & operator ++ ()
        {
            i ++;
            return * this;
        }
        // implement postfix ++ through prefix
        const Int operator ++ (int)
        {
            return ++ (* this);
        }
        // a + = through the constructor
        Int operator + = (int v)
        {
            return Int (i + v);
        }
    private:
        int i;
    };
    // operator + using an existing + =
    Int operator + (const Int & i, const int iv)
    {
        Int intVal (i);
        intVal + = iv;
        return intVal;
    }

    the key point - all the areas that could be taken out in one place - are taken out in it. So we guarantee a unified behavior for all homogeneous methods (+ = and + in our case) as well as a homogeneous behavior of class constructors. That is, there will be no behavior where one constructor works correctly, and the other does not initialize half of the variables.

    Every time you face a big task of remaking logic - think about it - maybe there is already a solution that does everything almost as you need. A solution that can be slightly adapted to your needs.

    It would be interesting to hear your simple solutions to complex problems.

    Also popular now: