воскресенье, 21 апреля 2013 г.

Complexity and Flexibility #2

What are the ways to balance complexity and flexibility? How do you manage the growing comlexity of your application?

There is a common situation that I met several times in my work: imagine that you have a class for object manager, say ObjMgr, and items that are stored in list inside it. Items can do some specific things and store some specific data. The code is approximately like this:

class ObjMgr
{
    List<Item> _items = new List<Item>();
    .......
    public void SetPrice(string id, double price) { _items.First(x = > x.Id == id).Price = price; }
}

class Item
{
    public string Id { get; }
    public double Price { get; set; }
    .......
}

Imagine further that the logic of setting price can be complicated. For example, you should check whether it is not negative.

Choice#1: There are several ways of doing it: in the SetPrice function of ObjMgr or in Price property of Item.
What would you prefer? It's worth doing it in the Item class, because, first, the logic can be much more complicated and it can differ for types of items. Well, it may be worth extracting the logic of setting price into separate class using Strategy pattern. If you use this approach you gain a flexibility by the price of comlexity (cause just checking for != 0 in SetPrice(...) is much simpler). Also if you do it in SetPrice(...) it doesn't satisfy dependency inversion principle from SOLID set of principles.

In the further work you may want to track if something is going wrong with new price you have set. For example, you may want to introduce some critical price difference and to log whether new price differs from the old too much. So you should log some events and thus there is a reference to some Logger in ObjMgr class.

Choice#2: There are several ways of doing it: the simplest one is to simply check the difference in ObjMgr's SetPrice function and to log the result using ObjMgr's logger, the second one is to check it inside the item.
Again, what would you prefer? For the same reasons, it's worth doing it in the item class and may be to use Strategy pattern again. And here another problem arises: Item doesn't include a reference to Logger. So you should solve this problem.

Choice#3: How to add logger reference to an Item? Here are some ways: first, you can add a reference directly and to make it the same reference as ObjMgr's one in the constructor; second, you can make a ref to ObjMgr's logger public and to add ref to ObjMgr itself; third you may make Logger class singleton or even a static class; forth you may add event to an Item and handle it in ObjMgr (or do the same thing using Observer pattern).
Again there is an option. Two first approaches are comparatively simple, but there is no need for Item to know about the existence of ObjMgr and logging is not a natural part of Item. Third approach also has a disadvantage - if it's a singleton, it cannot contain some info specific for current ObjMgr (for example, path to log into). Fourth one seems the most flexible but it is also the most complicated.

These choices are domain-specific (fo example current domain precludes that for all types of Items price should be checked not to be 0 with no special cases, so there is no need for Strategy pattern), but even with knowledge of domain this choice is fuzzy.

As a result, if you use the way of simplicity, the code becomes a total mess after some iterations of changes. The problem as I see it is that the way of flexibility turns the initial clear code turns into much bigger and more comlicated code which consists of many classes and hierarchies and which is in fact a different type of mess. There is a problem to reach a subtle balance between complexity and flexibility within current domain.



Комментариев нет:

Отправить комментарий