设计模式能够解决的问题
GOF指出了设计模式可以解决的问题,这些问题存在时可以考虑使用设计模式。
1.通过显示指定类创建对象
创建对象的最简单方法是采用New关键字直接调用类的构造函数,例如我们声明一个针对SQL Sever的数据库连接:
IdbConnection dc=new SqlDataConnection();
产生的问题是当我们希望采用Oracle数据库时,代码就要发生变化。更糟糕的是,针对不同的数据库,需要不同的代码版本。
因此我们希望在实例化时间接创建对象,与这类问题相关的设计模式有抽象工厂模式、工厂方法模式和原型模式。
2.紧耦合
紧耦合的类难以复用和维护,修改或删除一个类,必须修改与之相关联的类。一个设计不良的系统经常是铁板一块,修改部分内容都会牵涉到全部。
这里有一个紧耦合的例子,在一个类中我们希望用一个窗体来显示执行进度。这个窗体很简单,其中包括一个进度棒和显示进度的函数:
public void RefrdshProcess(int Max,int Current)
{
this.progressBarl.Minimum=0;
this.progressBarl.Maximum=Max;
this.progressBarl.value=Current;
}
为了显示进度,我们必须在方法的执行过程中调用这个函数:
public void Comp()
{
for(int I=0;I<1000;I++)
{//计算过程省略
frmprocess.RefreshProcess(1000,i);
}
}
这样,执行算法的业务类与显示进度窗体之间是紧耦合的。如果想修改显示进度的方式,则不得不修改计算类。尽管计算过程与进度显示实际上无关,甚至去掉显示进度功能也很困难。
在设计时,需要类之间是松耦合的,与此相关的模式有抽象工厂模式、命令模式、外观模式、中介者模式、观察者模式和职责链模式等。
3.通过生成子类扩展功能
如前所述,通过生成了子类扩展功能有很多问题,会引起子类数量的大量增加和类层次的增加通过对象组合技术实现功能扩展是更好的选择。
这方面的相关模式有桥接模式、职责链模式、组合模式、装饰模式、观察者模式和策略模式等。
4.对对象表示或实现的依赖
如果客户知道对象如何表示、保存、定位或实现,那么在对象发生变化时也可能发生变化,因此需要对客户隐藏这些信息。
让我们看一个表面上是松耦合,实际上是紧耦合的例子。数据集经常作为数据传输的方式,在传输过程中它隐藏了数据结构的差异。很多情况下会造成对象间潜在的依赖性,主要是对保存方式依赖性。假设类A负责从数据库中获得数据,并保存在数据集中,类B从类A获得数据并进行处理。类A符合接口C,并且有简单工厂负责创建类A,使类B不依赖于类A。在类B中:
Dim myB as InterfaceC=Bfactroy.CreateB();
Dim ds as DataSet=myB.GetData();
到这里,看起来没有任何耦合,但是ds中的数据结构是什么?假如类B中接下来处理ds中的表,那么类B必须“知道”ds中DataTable的结构;否则无法处理。定义 ds 的显然是类A,所以表面上类B并不依赖类A。但类A中数据集发生变化,类B也改期危机发生变化,甚至字段的对应关系都必须一致,这也是类B依赖于类A创建DataSet的策略。
相关的设计模式有抽象工厂模式、桥接模式、备忘录模式和代理模式等。
5.不能方便地修改类
很多情况下我们无法修改已存在的类,但又希望修改这个类。例如希望采用RealPlay构件作为复读机软件的后台播放器,但我们却无法修改它。
相关的设计模式有适配器模式、装饰模式和访问者模式等。
6.对算法的依赖
算法实现的目的是不变的,但算法本身却不是一成不变的。
引起算法变化的原因很多,算法的优化、统计体系的变化,以及业务规则的变化等。例如,一个压缩算法经过改进可以大大提高压缩比,个人所得税的计算方法随着国家税收政策的调整而改变等。因此使用算法的客户不能依赖于算法,需要将算法孤立起来。
与此相关的设计模式有生成器模式、迭代模式、策略模式、模板方法模式和访问者模式等。
7.对软硬件环境的依赖
应用程序总是要与其运行环境打交道,我们希望屏蔽其对运行环境的依赖,以确保可移植性和可维护性。例如开发一款手机游戏,自然不希望其依赖某个品牌的API。
与此相关的设计模式有抽象工厂模式和桥接模式等。