开发者

Open/Closed for flexible software

开发者 https://www.devze.com 2023-03-03 03:05 出处:网络
The title may not be too descriptive, but I couldn\'t think of a better one. I\'m sorry for that. So, the problem I am having here is one I have come across a couple of times now. It\'s really about

The title may not be too descriptive, but I couldn't think of a better one. I'm sorry for that.

So, the problem I am having here is one I have come across a couple of times now. It's really about design patterns and principles, and is language independent as long as you have OO facilities in your language. I'll take you through the current prob开发者_如何学JAVAlem I am having, as it is hard to explain what the problem is without an actual example.

So, I am using some classes to describe logic statements here. Think of something like this:

condition = new And(new Equals("x", 5),
                    new EqualsOrOver("y", 20));

Now this is all dandy and all, but the problem comes when I want to use this class system. Right now, I am making a system where the condition needs to be translated to an SQL WHERE clause.

I could do this in several ways, but none seem to adhere to the Open/Closed principle. For example, I could have the Database class parse the condition and make it SQL. The problem is that in this way, I can't extend my Condition without the need to change my Database, thus we're not following Open/Closed.

Another way - which would seem logical here - is to add a toSQL() function to my Conditions. However, in that case, I am unable to swap my Database for one that (just to name something) uses XML, that doesn't want the condition in SQL format.

One way I have worked around this problem in the past has been to use a factory. The factory method in this case would look something like this:

turnIntoSQLCondtion(Condition c)
{
    if (c instanceof Equals)
    {
         return new SQLEquals(c);
    }
    else if (c instanceof EqualsOrOver)
    {
         return new SQLEqualsOrOver(c);
    }
    else if (c instanceof And)
    {
         return SQLAnd(c);
    }
}

This isn't all that nice, but it will reduce the violation of Open/Closed.

Now, you can subclass your Database just fine. You will just have to subclass your factory as well, and you will have to make a new bunch of Condition classes specific to your Database as well. You can also work your magic on the Condition classes as well. You can make a new Conditions, but you will have to make companion classes for each Database as well. Lastly, you will have to modify your factories.

It's the smallest violation we have seen so far, but we are still violating Open/Closed. But as a matter of fact, I would much rather not violate it at all. Is there a way to do this while sticking to Open/Closed all the way?


This isn't all that nice, but it will reduce the violation of Open/Closed

Actually no. That method is a violation of OSP ;)

The problem is not as hard as you might think. Simple create a SQL statement factory where you map each Condition class to a SQL condition class.

    public class SqlFactory
    {
        private Dictionary<Type, Delegate> _factoryMethods
            = new Dictionary<Type, Delegate>();

        public void Assign<T>(Func<T, ISqlCondition> factoryMethod)
            where T : ICondition
        {
            _factoryMethods.Add(typeof (T), factoryMethod);
        }

        public ISqlCondition Create<T>(T source) where T : ICondition
        {
            Delegate factory;
            if (!_factoryMethods.TryGetValue(source.GetType(), out factory))
                return null;

            return ((Func<T, ISqlCondition>) factory)(source);
        }
    }

Usage:

        SqlFactory factory = new SqlFactory();
        factory.Assign<And>(obj => new SqlAnd(obj.Value));

        var and = new And();
        var sqlAnd = factory.Create(and);


I'm wondering if some extension by the Decorator patterns would be useful here ?

You'd want to define your predicates almost as a kind of DSL, or a list of rules that can be parsed appropriately.

If you decorate your WherePredicate as a SqlPredicate, then it would read this set of rules and return a SQL statement; an XmlPredicate would do similar.

That would leave you in a nice open/closed situation, Open to extend by adding a new decorator and closed for modification as you the list of rules is inviolable.


Protected Variations (PV) strives to allow variations in one zone without requiring modification in another. An indirection (delegation) is what @jgauffin proposed, but it only protects one side as you point out. Perhaps you can also use data-driven design (e.g., property file) for different Storage types to protect variations in that direction. I thought of Hibernate and Data Mapper when reading your question.

0

精彩评论

暂无评论...
验证码 换一张
取 消