浅析设计模式之抽象工厂模式

来源:互联网 发布:淘宝美特斯邦威官方店 编辑:程序博客网 时间:2024/05/22 04:48
一、设计模式定义、来历

     怎样重复问题的解决方案? 

     类似的这些问题在开发中经常遇到,经过前人的整理得出的一些好的解决方案,面向对象设计模式就是为了实现面向对象的一些原则的。 

     在群和论坛上很多人在讨论数据库中等其他领域是否有设计模式?回答是肯定的,设计模式只是前人对重复问题总结的一些经验。只要这个行业存在就有自己的设计模式。 

     设计模式起源于建筑,首先由建筑设计师亚历山大提出的,然后经过前人推广到软件设计行业来。在他的那本著名“模式语言”(A Pattern Language)一书中,他写道:“每个模式都描述着一种在我们的环境中一遍又一遍地出现的问题,并因此描述了对该问题的核心解决方案。以此方式你可以使用该方案上百万次,而从不需要重复作同样的事情。” 

     面向对象设计模式让你的设计更好的符合面向对象设计的一些原则,更好的解决日益变化的软件需求。 

     以下是面向对象的一些设计原则:

•“开-闭”原则(Open-Closed Principle,OCP)封装的问题 

     一个软件实体应当对扩展开放,对修改关闭。 你添加新功能的时候应该只是向代码集中添加新的代码不应该修改原来的代码。

•里氏代换原则(Liskov Substitution Principle, LSP) 职责的问题 

     LSP原则要求子类可以无条件的替代父类,子类不能对父类没有暴露的接口进行扩展,客户要调用功能只能通过父类暴露的接口来调用用不能擅自向子类调用。

•依赖倒转原则(dependence inversion principle, DIP) 耦合度问题 

     依赖倒转原则就是要实现依赖于抽象,抽象不要依赖于实现。要针对接口编程,不要针对实现编程。

•合成/聚合复用原则(Composite/Aggregate Reuse Principle或CARP) 复用问题 

     在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用这些对象的目的。

•迪米特法则(Law of Demeter,LoD) 

     一个软件实体应当尽可能少的与其他实体发生相互作用。

•接口隔离原则(interface separate principle, ISP) 职责单一 

     使用多个专门的接口比使用单一的总接口要好。也就是说,一个类对另外一个类的依赖性应当是建立在最小的接口上。 

     软件行业最早出现的设计模式著作是GOF(Gang Of Fours)四人组所著的那本《设计模式-可复用面向对象软件基础》,这本书将设计模式分为三大类:

•创建型模式: 创建型模式是关注对象的创建实例化的。它将对象的创建与对象的实现、描绘相分离。

•结构型模式: 结构型模式关注复杂对象的构建。将小粒度的对象组合成大的对象。

•行为型模式: 行为型模式关注对象的职责以及他们之间如何通信的问题
 

 

二、抽象工厂模式的使用场景 

    今天要谈的抽象工厂模式属于对象创建型模式。 

    创建型模式抽象了对象实例化的过程,它帮助系统不依赖于对象如何创建,如何实现,何时创建。个类创建型模式使用继承使对象创建多样化,一个对象创建模式将对象的创建代理到其他类。 

    那抽象工厂模式是为了解决什么问题的呢?给了我们怎样的设计思路?在软件开发中我们经常会碰到一系列相关的对象需要创建,如果按照常规做法我们就要为不同的对象创建编写不同的代码,复用性和可维护性都降低了。而且这些相关对象创建的方式也许不同,那么客户代码创建的时候就要针对不同的对象编码,对象创建的方式还是一个容易改变的地方。基于这样的情况提出了抽象工厂模式,抽象工厂模式为创建一系列相关对象提供了统一的接口,客户只要调用这个接口即可,封装了变化,隔离了变化,让客户代码稳定起来。 

    比如这样一个情况,我们做了一个桌面软件,这个软件的界面是可以改变的,它有几种风格:XP风格、Win2000风格、苹果机风格。 

    这个软件就是显示一个窗口,窗口有标题栏、滚动条,XP风格的界面有它自己的标题栏和滚动条,而苹果机风格的又不一样。 

    我们常常怎么做?

switch(type){case "XP" :setTitle(new XPTitle());setScrollbar(new XPScrollbar());break;case "win2000" :setTitle(new win2000Title());setScrollbar(new win2000Scrollbar());break;case "macos" :setTitle(new macosTitle());setScrollbar(new macosScrollbar());break;}

    这样做有什么坏处呢,这个例子太小实际上没有什么坏处,这样写可以。但是人总会出错,有一次你这样写了:

case "win2000" :setTitle(new win2000Title());setScrollbar(new XPScrollbar());break;

 

    你的界面将是Win2000的标题栏,XP风格的滚动条,这就造成了不一致性抽象工厂就是为了解决这种一系列相关对象创建工作的。


图1:抽象工厂的类图

    在上面这个例子中标题栏和滚动条都是我们的产品,我们还应该写一个WindowManager类,专门来管理这些产品的创建的,而我们的WinXP团队、Win2000团队、苹果机团队实现这个WindowManager,WinXP团队只会制造出XP风格的产品。