打开/关闭灵活的软件

时间:2011-05-06 12:06:06

标签: design-patterns factory-pattern open-closed-principle

标题可能不太具描述性,但我想不出更好的标题。对不起,我很抱歉。

所以,我在这里遇到的问题是我现在遇到的问题。它实际上是关于设计模式和原则,并且只要您拥有您所用语言的OO设施,它就是独立于语言的。我将带您了解我遇到的当前问题,因为如果没有实际的例子,很难解释问题是什么。

所以,我在这里使用一些类来描述逻辑语句。想想这样的事情:

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

现在这一切都很花花公子,但是当我想要使用这个类系统时问题就来了。现在,我正在创建一个系统,其中条件需要转换为SQL WHERE子句。

我可以通过多种方式做到这一点,但似乎没有人遵守开放/封闭原则。例如,我可以让Database类解析条件并使其成为SQL。问题在于,通过这种方式,我无法在不需要更改Condition的情况下扩展Database,因此我们不会关闭打开/关闭。

另一种方式 - 在这里看似合乎逻辑 - 是向toSQL()添加Condition函数。但是,在这种情况下,我无法将我的数据库交换为(只是为了命名)使用XML,而不希望SQL格式的条件。

过去我解决这个问题的一种方法是使用工厂。在这种情况下,工厂方法看起来像这样:

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);
    }
}

这不是很好,但它会减少违反Open / Closed。

现在,您可以将数据库子类化得很好。您也必须将工厂子类化,并且您还必须创建一组特定于Condition的新Database类。您也可以在Condition类中运用您的魔法。您可以创建一个新的Condition,但您还必须为每个Database创建伴随类。最后,您将不得不修改您的工厂。

这是我们迄今为止看到的最小违规行为,但我们仍然违反了开放/封闭。但事实上,我宁愿不要违反它。有没有办法在坚持打开/关闭的同时一直这样做?

3 个答案:

答案 0 :(得分:1)

  

这不是那么好,但它会减少违反Open / Closed

其实没有。该方法违反了OSP;)

问题并不像你想象的那么难。简单地创建一个SQL语句工厂,将每个Condition类映射到SQL条件类。

    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);
        }
    }

用法:

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

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

答案 1 :(得分:0)

我想知道Decorator模式的某些扩展是否在这里有用?

您希望将谓词定义为一种DSL,或者可以正确解析的规则列表。

如果你将WherePredicate装饰为SqlPredicate,那么它会读取这组规则并返回一个SQL语句;一个XmlPredicate会做类似的事情。

这将使您处于一个良好的开放/关闭状态,通过添加新的装饰器来打开以扩展,并且因为您的规则列表不可侵犯而关闭以进行修改。

答案 2 :(得分:0)

Protected Variations (PV)努力允许在一个区域中进行变化,而无需在另一个区域进行修改。间接(委托)是@jgauffin提出的,但它只会在你指出的时候保护一方。也许您也可以使用不同Storage类型的数据驱动设计(例如,属性文件)来保护该方向的变化。在阅读你的问题时,我想到了Hibernate和Data Mapper