`
liuyanhui
  • 浏览: 37945 次
  • 性别: Icon_minigender_1
  • 来自: 沈阳
最近访客 更多访客>>
社区版块
存档分类
最新评论

设计模式初学者系列-开篇以及抽象工厂模式

    博客分类:
  • .NET
阅读更多
一、设计模式定义、来历

类似的一些问题在开发中经常遇到,经过前人的整理得出的一些好的解决方案,面向对象设计模式就是为了实现面向对象的一些原则的。
那么我们可以得到设计模式的定义:设计模式就是重复问题的一些好的解决方案

     设计模式起源于建筑,首先由建筑设计师亚历山大提出的,然后经过前人推广到软件设计行业来。在他的那本著名“模式语言”(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风格的界面有它自己的标题栏和滚动条,而苹果机风格的又不一样。
我们常常怎么做?

 1switch(type)
 2{
 3case "XP" :
 4setTitle(new XPTitle());
 5setScrollbar(new XPScrollbar());
 6break;
 7case "win2000" :
 8setTitle(new win2000Title());
 9setScrollbar(new win2000Scrollbar());
10break;
11case "macos" :
12setTitle(new macosTitle());
13setScrollbar(new macosScrollbar());
14break;
15}

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


1case "win2000" :
2setTitle(new win2000Title());
3setScrollbar(new XPScrollbar());
4break;
你的界面将是Win2000的标题栏,XP风格的滚动条。如果要添加新的样式呢?或者更改现在样式,需求总是在不断的改变,你会陷入无穷无尽的代码修改当中。幸亏有我们的抽象工厂模式:创建一系列相关对象,将我们的产品和产品创建工作分离。
在上面这个例子中标题栏和滚动条都是我们的产品,我们还应该写一个WindowManager类,专门来管理这些产品的创建的,而我们的WinXP团队、Win2000团队、苹果机团队实现这个WindowManager,WinXP团队只会制造出XP风格的产品。(这个例子我就不实现了,希望各位同学在看完文章后能够自己实现之,呵呵)。

三、设计模式实例

下面举一个网上非常流行的例子。
  带着你的女友去快餐店吃东西,比如麦当劳、肯德基,你走进快餐店只需要对服务员说“来份鸡腿”(统一的接口),如果是麦当劳服务员就会用麦当劳专用的盘子装着麦当劳的鸡腿拿给你,走进肯德基也一样,你还是只需要说“来份鸡腿”,不需要改变你的用词,而且也不会出现麦当劳的盘子装着肯德基的(一系列相关类)鸡腿。
在这里 麦当劳、肯德基就是生产食品产品的工厂、盘子和鸡腿都是他们的产品。 这个实例我用一个演进的过程来描述,首先我们按照Gof所描述的抽象工厂模式来完成这个示例的代码,然后,为了验证开闭原则(对添加打开,对修改关闭),假设本地新开了一家快餐厅,向系统的服务器层添加一个快餐厅(全聚德)来看看是否对系统造成修改的影响,在.net里我们可以利用反射功能将这个抽象工厂模式演进为一个反射工厂模式,利用反射后我们甚至可以去掉麦当劳和肯德基等餐厅的实现,但是如果女友想去别的餐厅吃的时候girlfriend类还是要改变,进一步我们可以利用配置文件来配置所要去的餐厅这样系统就稳定多了,当要添加餐厅并去新餐厅的时候只需要添加新的特性修改配置文件。 本着“代码就是设计”(XP)的思想,以下的过程大部分用代码来说明,代码中有注释。


1//代码清单
 2using System;
 3namespace Jurassic.Training.AbstractFactory
 4{
 5    /**//// <summary>
 6    /// 餐厅类抽象工厂
 7    /// </summary>
 8      public abstract class Restaurant
 9    {
10        /**//// <summary>
11        /// 创建一个盘子类
12        /// </summary>
13        /// <returns>盘子</returns>
14              public abstract Plate CreatePlate();
15        
16        /**//// <summary>
17        /// 创建一个鸡腿类
18        /// </summary>
19        /// <returns>鸡腿</returns>
20              public abstract Drumstick CreateDrumstick();
21    }
22}
23using System;
24namespace Jurassic.Training.AbstractFactory
25{
26    /**//// <summary>
27    /// 肯德基餐厅
28    /// </summary>
29      public class KFC : Restaurant
30    {
31        /**//// <summary>
32        /// 创建一个肯德基的盘子
33        /// </summary>
34        /// <returns>肯德基盘子</returns>
35        public override Plate CreatePlate()
36        {
37            return new KFCPlate();
38        }
39        /**//// <summary>
40        /// 创建一个肯德基鸡腿
41        /// </summary>
42        /// <returns>肯德基鸡腿</returns>
43            public override Drumstick CreateDrumstick()
44        {
45            return new KFCDrumstick();
46        }
47    }
48}
49using System;
50namespace Jurassic.Training.AbstractFactory
51{
52    /**//// <summary>
53    /// 麦当劳餐厅
54    /// </summary>
55      public class MDL : Restaurant
56    {
57        /**//// <summary>
58        /// 创建一个麦当劳盘子
59        /// </summary>
60        /// <returns>麦当劳盘子</returns>
61         public override Plate CreatePlate()
62        {
63            return new MDLPlate();
64        }
65        /**//// <summary>
66        /// 创建一个麦当劳鸡腿
67        /// </summary>
68        /// <returns>麦当劳鸡腿</returns>
69            public override Drumstick CreateDrumstick()
70        {
71            return new MDLDrumstick();
72        }
73    }
74}
75using System;
76namespace Jurassic.Training.AbstractFactory
77{
78    /**//// <summary>
79    /// 盘子产品父类
80    /// </summary>
81      public abstract class Plate
82    {
83        /**//// <summary>
84        /// 盘子装载方法
85        /// </summary>
86        /// <returns></returns>
87            public abstract string Load();
88    }
89} 1using System;
 2namespace Jurassic.Training.AbstractFactory
 3{
 4/**//// <summary>
 5/// 肯德基的盘子
 6/// </summary>
 7public class KFCPlate : Plate
 8{
 9public override string Load()
10{
11return "肯德基的盘子装";
12}
13}
14}
15using System;
16namespace Jurassic.Training.AbstractFactory
17{
18/**//// <summary>
19/// 麦当劳的盘子
20/// </summary>
21  public class MDLPlate : Plate
22{
23public override string Load()
24{
25return "麦当劳的盘子装";
26}
27}
28}
29using System;
30namespace Jurassic.Training.AbstractFactory
31{
32/**//// <summary>
33/// 鸡腿产品父类
34/// </summary>
35  public abstract class Drumstick
36{
37public abstract string Name{get;}
38}
39}
40using System;
41namespace Jurassic.Training.AbstractFactory
42{
43/**//// <summary>
44/// 肯德基鸡腿
45/// </summary>
46  public class KFCDrumstick : Drumstick
47{
48public override string Name
49{
50get{ return "肯德基的鸡腿"; }
51}
52}
53}
54using System;
55namespace Jurassic.Training.AbstractFactory
56{
57/**//// <summary>
58/// 麦当劳鸡腿
59/// </summary>
60  public class MDLDrumstick : Drumstick
61{
62public override string Name
63{
64get { return "麦当劳的鸡腿"; }
65}
66}
67}
68using System;
69namespace Jurassic.Training.AbstractFactory
70{
71/**//// <summary>
72/// 女友,模拟客户类
73/// </summary>
74  public class Girlfriend
75{
76public static void Main()
77{
78//走进肯德基
79        Restaurant restaurant = new KFC();
80GoRestaurant(restaurant);
81Console.ReadLine();
82}
83public static void GoRestaurant(Restaurant restaurant)
84{
85Plate p = restaurant.CreatePlate();
86Drumstick d = restaurant.CreateDrumstick();
87Console.WriteLine(p.Load() + d.Name);
88}
89}
90}

91现在我们向系统添加一个餐厅:全聚德餐厅。
 1using System;
 2namespace Jurassic.Training.AbstractFactory
 3{
 4/**//// <summary>
 5/// 全聚德餐厅
 6/// </summary>
 7  public class QJD : Restaurant
 8{
 9/**//// <summary>
10/// 创建全聚德盘子
11/// </summary>
12/// <returns></returns>
13      public override Plate CreatePlate()
14{
15return new QJDPlate();
16}
17/**//// <summary>
18/// 创建全聚德鸡腿
19/// </summary>
20/// <returns></returns>
21        public override Drumstick CreateDrumstick()
22{
23return new QJDDrumstick();
24}
25}
26}
27using System;
28namespace Jurassic.Training.AbstractFactory
29{
30/**//// <summary>
31/// 全聚德盘子
32/// </summary>
33    public class QJDPlate : Plate
34{
35public override string Load()
36{
37return "全聚德的盘子装";
38}
39}
40}
41using System;
42namespace Jurassic.Training.AbstractFactory
43{
44/**//// <summary>
45/// 全聚德鸡腿
46/// </summary>
47    public class QJDDrumstick : Drumstick
48{
49public override string Name
50{
51get{return "全聚德鸡腿";}
52}
53}
54}
55

为了可以进全聚德吃鸡腿我们需要对客户类进行修改
 1using System;
 2namespace Jurassic.Training.AbstractFactory
 3{
 4public class Girlfriend
 5{
 6public static void Main()
 7{
 8//走进肯德基
 9//Restaurant restaurant = new KFC();
10//GoRestaurant(restaurant);
11//走进麦当劳
12//Restaurant restaurant = new MDL();
13//GoRestaurant(restaurant);
14//走进全聚德
15        Restaurant restaurant = new QJD();
16GoRestaurant(restaurant);
17Console.ReadLine();
18}
19public static void GoRestaurant(Restaurant restaurant)
20{
21Plate p = restaurant.CreatePlate();
22Drumstick d = restaurant.CreateDrumstick();
23Console.WriteLine(p.Load() + d.Name);
24}
25}
26}
27


但是整个过程我们并没有对系统的基础框架进行修改,只是添加了新的特性,符合开-闭原则。  仔细审视了工厂部分的实现,我们发现几个餐厅的实现部分都很雷同,我们请来了.net的反射,将几个餐厅实现部分都去除了,代码大大减少。 要使用.net的反射我们将餐厅的抽象类修改为如下所示:

 1using System;
 2using System.Reflection;
 3namespace Jurassic.Training.AbstractFactory
 4{
 5public class Restaurant
 6{
 7private string _plateFile;
 8private string _drumstickFile;
 9private string _plate;
10private string _drumstick;
11public Restaurant(string type)
12{
13_plateFile = string.Format("{0}Plate.dll",type);
14_drumstickFile = string.Format("{0}Drumstick.dll",type);
15_plate = string.Format("Jurassic.Training.AbstractFactory.{0}Plate",type);
16_drumstick = string.Format("Jurassic.Training.AbstractFactory.{0}Drumstick",type);
17}
18public Plate CreatePlate()
19{
20Plate plate = Helper(_plateFile,_plate) as Plate;
21return plate;
22}
23public Drumstick CreateDrumstick()
24{
25Drumstick drumstick = Helper(_drumstickFile,_drumstick) as Drumstick;
26return drumstick;
27}
28/**//// <summary>
29/// 根据程序集文件名称和类型实例化类型
30/// </summary>
31/// <param name="fileName"></param>
32/// <param name="type"></param>
33/// <returns></returns>
34      private object Helper(string fileName,string type)
35{
36Assembly asm = Assembly.LoadFrom(fileName);
37object o = asm.CreateInstance (type);
38return o;
39}
40}
41}
42

这样我们可以把KFC.cs,MDL.cs,QJD.cs全部去掉了,要调用新的反射工厂模式的实现需要修改girlfriend如下:

 1using System;
 2namespace Jurassic.Training.AbstractFactory
 3{
 4public class Girlfriend
 5{
 6public static void Main()
 7{
 8//走进全聚德
 9        Restaurant restaurant = new Restaurant("QJD");
10GoRestaurant(restaurant);
11Console.ReadLine();
12}
13public static void GoRestaurant(Restaurant restaurant)
14{
15Plate p = restaurant.CreatePlate();
16Drumstick d = restaurant.CreateDrumstick();
17Console.WriteLine(p.Load() + d.Name);
18}
19}
20}

虽说代码减少不少,但是如果我们要更换就餐的餐厅还是要修改girlfriend类,然后重新编译,这个时候我们想到把这个变化存储在应用程序之外,.net里的配置文件就是存储这个东西的好地方。代码进一步演进,如下:

 1using System;
 2using System.Configuration;
 3namespace Jurassic.Training.AbstractFactory
 4{
 5public class Girlfriend
 6{
 7public static void Main()
 8{
 9//走进全聚德,从配置文件里获取信息
10        Restaurant restaurant = new Restaurant(ConfigurationManager.AppSettings["type"]);
11GoRestaurant(restaurant);
12Console.ReadLine();
13}
14public static void GoRestaurant(Restaurant restaurant)
15{
16Plate p = restaurant.CreatePlate();
17Drumstick d = restaurant.CreateDrumstick();
18Console.WriteLine(p.Load() + d.Name);
19}
20}
21}

加上配置文件
Girlfriend.exe.config

1<?xml version="1.0" encoding="utf-8" ?>
2<configuration>
3<appSettings>
4<add key="type" value="KFC"/>
5</appSettings>
6</configuration>
7
 


上面这个例子从Gof原版的抽象工厂模式说起,演进到.net里面抽象工厂模式的变化,最后用反射和配置文件构造出一个非常松耦合的系统。

四、现实中的抽象工厂模式

    微软推出.net的时候,为了和Java相比较,针对Java的PetStore推出了PetShop 3.0,Petshop是一个经典的三层架构实例,它提供了两种数据库支持:SQL Server,Oracle,提供多数据库支持的时候就是使用的.net中抽象工厂模式的变体:反射工厂模式

分享到:
评论

相关推荐

    设计模式(简单工厂,工厂,抽象工厂)简单例子.rar

    在给定的压缩包文件中,我们关注的是三种特定的创建型设计模式:简单工厂模式、工厂方法模式以及抽象工厂模式。这三种模式都是关于对象创建的,但它们在灵活性和抽象层次上有所不同。 **简单工厂模式**,又称为静态...

    《Java设计模式》课后答案-刘伟.rar

    创建型设计模式如单例(Singleton)、工厂方法(Factory Method)、抽象工厂(Abstract Factory)、建造者(Builder)和原型(Prototype),它们主要关注对象的创建过程,使得代码更加灵活且易于扩展。 结构型设计...

    C#抽象工厂实例,设计模式

    初学者必备!设计模式 源码! 优点 l 分离了具体的类。抽象工厂模式帮助你控制一个应用创建的对象的类,因为一个工厂封装创建产品对象的责任和过程。它将客户和类的实现分离,客户通过他们的抽象接口操纵实例,...

    设计模式PPT---25种设计模式详解

    这份PPT涵盖了设计模式的全貌,对于初学者来说,是一个很好的学习资源。通过深入学习这些模式,开发者可以更好地理解和应用面向对象设计原则,提升代码质量和可维护性。同时,理解并熟练运用设计模式,也是成为一名...

    简单工厂和抽象工厂的Demo

    在软件设计模式中,"简单工厂"和"抽象工厂"是两种常见的创建型设计模式,它们主要用于对象的创建。这两种模式都是为了将对象的创建过程封装起来,使得客户端代码无需关心具体的实现细节,只需调用工厂提供的接口即可...

    Java和Android源码设计模式 2017-03-21

    ### Java和Android源码设计模式解析 #### 前言:设计模式的重要性 设计模式是软件工程中的一个重要概念,它代表了一种最佳实践,能够帮助开发者...无论是对于初学者还是资深开发者来说,掌握设计模式都是非常重要的。

    模式之一工厂模式-------------------------

    工厂模式是设计模式中的一种基础模式,它在Java编程中占据着重要的地位,尤其对于初学者来说,理解和掌握它是至关重要的。工厂模式属于创建型设计模式,它的主要目的是通过提供一个统一的接口来创建对象,使得客户端...

    设计模式-工厂模式 Java源码

    在Java项目中,工厂模式被广泛应用,尤其是在框架和库的设计中,如Spring框架中的Bean工厂,以及各种DAO(数据访问对象)的工厂等。 在这个名为"factory"的压缩包文件中,很可能是包含了上述三种工厂模式的Java源码...

    设计模式初学者指南(带完整目录扫描版)-6

    本指南是专为初学者设计的,旨在帮助他们理解并掌握设计模式的基本概念和应用。 设计模式通常分为三类:创建型模式、结构型模式和行为型模式。创建型模式关注对象的创建,如单例模式(Singleton)、工厂模式...

    一例读懂设计模式-单例模式、简单工厂模式.zip

    本资源“一例读懂设计模式-单例模式、简单工厂模式.zip”旨在帮助初学者通过实例深入理解这两种重要设计模式:单例模式和简单工厂模式。以下是关于这两个模式的详细讲解。 1. **单例模式**: 单例模式是一种限制类...

    工厂模式-设计模式

    这个压缩包文件可能是关于如何在实际项目中运用工厂模式的教学材料,特别适合那些正在学习设计模式的初学者。 首先,我们需要理解工厂模式的核心概念。工厂模式是一种创建型设计模式,它提供了一个创建对象的最佳...

    Oracle Database 11g初学者指南--详细书签版

    《Oracle Database 11g初学者指南--详细书签版》是一本为初学者量身定制的指南,内容涵盖了Oracle Database 11g的基础知识、安装、管理、编程、备份与恢复、高可用性解决方案以及大型数据库特性等方面。 一、核心...

    JavaScript 设计模式(高清扫描版本)- 张容铭

    1. 创建型模式:这些模式关注对象的创建过程,例如单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式。在JavaScript中,单例模式常用于确保一个类只有一个实例,而建造者模式则允许复杂对象的构建过程...

    java设计模式经典教程

    - **新手开发者**:对于初学者来说,学习设计模式可以帮助他们更快地掌握软件设计的基本原则和技术。 - **有经验的开发者**:对于经验丰富的开发者,学习设计模式可以进一步提升他们的技能,帮助他们更高效地解决...

    CC++与设计模式基础课程-讲义

    创建型模式关心对象的创建过程,包括工厂方法模式、抽象工厂模式、建造者模式、原型模式和单例模式等。结构型模式关心类和对象的组合,包括代理模式、装饰者模式、适配器模式、桥接模式、组合模式、外观模式和享元...

    抽象工厂模型demo

    抽象工厂类的使用,简单的demo,方便初学者理解,设计模式。

    《Java设计模式》课程设计报告.docx

    这个项目涵盖了四种重要的设计模式:单例模式、代理模式、建造者模式和抽象工厂模式,为初学者提供了一个良好的实践平台。 1. **单例模式**: 单例模式是一种确保一个类只有一个实例并提供全局访问点的设计模式。...

    设计模式精解-GoF 23种设计模式解析附C++实现源码.pdf

    - **1.2 Abstract Factory模式**:抽象工厂模式提供了一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。 - **1.3 Singleton模式**:单例模式确保一个类只有一个实例,并提供一个全局访问点...

    C++设计模式-基于QT4开源跨平台开发框架 源码

    设计模式分为三大类:创建型模式(如单例、工厂方法、抽象工厂)、结构型模式(如代理、装饰器、适配器)和行为型模式(如观察者、策略、命令)。这些模式都是在特定上下文中解决设计问题的模板,它们可以提高代码的...

    Head First设计模式(完整高清版).pdf

    - 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。 - 建造者模式:将复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 - 原型模式:通过复制已有...

Global site tag (gtag.js) - Google Analytics