程序设计方法学23种设计模式.docx
《程序设计方法学23种设计模式.docx》由会员分享,可在线阅读,更多相关《程序设计方法学23种设计模式.docx(152页珍藏版)》请在冰豆网上搜索。
程序设计方法学23种设计模式
(1)单例模式
说到单例模式,大家第一反应应该就是——什么是单例模式?
,从“单例”字面意思上理解为——一个类只有一个实例,所以单例模式也就是保证一个类只有一个实例的一种实现方法罢了(设计模式其实就是帮助我们解决实际开发过程中的方法,该方法是为了降低对象之间的耦合度,然而解决方法有很多种,所以前人就总结了一些常用的解决方法为书籍,从而把这本书就称为设计模式),下面给出单例模式的一个官方定义:
确保一个类只有一个实例,并提供一个全局访问点。
为了帮助大家更好地理解单例模式,大家可以结合下面的类图来进行理解,以及后面也会剖析单例模式的实现思路:
了解完了一些关于单例模式的基本概念之后,下面就为大家剖析单例模式的实现思路的,因为在我自己学习单例模式的时候,咋一看单例模式的实现代码确实很简单,也很容易看懂,但是我还是觉得它很陌生(这个可能是看的少的,或者自己在写代码中也用的少的缘故),而且心里总会这样一个疑问——为什么前人会这样去实现单例模式的呢?
他们是如何思考的呢?
后面经过自己的琢磨也就慢慢理清楚单例模式的实现思路了,并且此时也不再觉得单例模式陌生了,下面就分享我的一个剖析过程的:
我们从单例模式的概念(确保一个类只有一个实例,并提供一个访问它的全局访问点)入手,可以把概念进行拆分为两部分:
(1)确保一个类只有一个实例;
(2)提供一个访问它的全局访问点;下面通过采用两人对话的方式来帮助大家更快掌握分析思路:
菜鸟:
怎样确保一个类只有一个实例了?
老鸟:
那就让我帮你分析下,你创建类的实例会想到用什么方式来创建的呢?
新手:
用new关键字啊,只要new下就创建了该类的一个实例了,之后就可以使用该类的一些属性和实例方法了
老鸟:
那你想过为什么可以使用new关键字来创建类的实例吗?
菜鸟:
这个还有条件的吗?
.........,哦,我想起来了,如果类定义私有的构造函数就不能在外界通过new创建实例了(注:
有些初学者就会问,有时候我并没有在类中定义构造函数为什么也可以使用new来创建对象,那是因为编译器在背后做了手脚了,当编译器看到我们类中没有定义构造函数,此时编译器会帮我们生成一个公有的无参构造函数)
老鸟:
不错,回答的很对,这样你的疑惑就得到解答了啊
菜鸟:
那我要在哪里创建类的实例了?
老鸟:
你傻啊,当然是在类里面创建了(注:
这样定义私有构造函数就是上面的一个思考过程的,要创建实例,自然就要有一个变量来保存该实例把,所以就有了私有变量的声明,但是实现中是定义静态私有变量,朋友们有没有想过——这里为什么定义为静态的呢?
对于这个疑问的解释为:
每个线程都有自己的线程栈,定义为静态主要是为了在多线程确保类有一个实例)
菜鸟:
哦,现在完全明白了,但是我还有另一个疑问——现在类实例创建在类内部,那外界如何获得该的一个实例来使用它了?
老鸟:
这个,你可以定义一个公有方法或者属性来把该类的实例公开出去了(注:
这样就有了公有方法的定义了,该方法就是提供方法问类的全局访问点)
通过上面的分析,相信大家也就很容易写出单例模式的实现代码了,下面就看看具体的实现代码(看完之后你会惊讶道:
真是这样的!
):
///单例模式的实现
publicclassSingleton
{
//定义一个静态变量来保存类的实例
privatestaticSingletonuniqueInstance;
//定义私有构造函数,使外界不能创建该类实例
privateSingleton(){}
///定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
publicstaticSingletonGetInstance()
{
//如果类的实例不存在则创建,否则直接返回
if(uniqueInstance==null)
{
uniqueInstance=newSingleton();
}
returnuniqueInstance;
}
}
上面的单例模式的实现在单线程下确实是完美的,然而在多线程的情况下会得到多个Singleton实例,因为在两个线程同时运行GetInstance方法时,此时两个线程判断(uniqueInstance==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了,既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使GetInstance方法在同一时间只运行一个线程运行就好了,也就是我们线程同步的问题了(对于线程同步大家也可以参考我线程同步的文章),具体的解决多线程的代码如下:
///单例模式的实现
publicclassSingleton
{
//定义一个静态变量来保存类的实例
privatestaticSingletonuniqueInstance;
//定义一个标识确保线程同步
privatestaticreadonlyobjectlocker=newobject();
//定义私有构造函数,使外界不能创建该类实例
privateSingleton(){}
///定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点
publicstaticSingletonGetInstance()
{
//当第一个线程运行到这里时,此时会对locker对象"加锁",
//当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁
//lock语句运行完之后(即线程运行完之后)会对该对象"解锁"
lock(locker)
{
//如果类的实例不存在则创建,否则直接返回
if(uniqueInstance==null)
{
uniqueInstance=newSingleton();
}
}
returnuniqueInstance;
}
}
(2)简单工厂模式
说到简单工厂,自然的第一个疑问当然就是什么是简单工厂模式了?
在现实生活中工厂是负责生产产品的,同样在设计模式中,简单工厂模式我们也可以理解为负责生产对象的一个类, 我们平常编程中,当使用"new"关键字创建一个对象时,此时该类就依赖与这个对象,也就是他们之间的耦合度高,当需求变化时,我们就不得不去修改此类的源码,此时我们可以运用面向对象(OO)的很重要的原则去解决这一的问题,该原则就是——封装改变,既然要封装改变,自然也就要找到改变的代码,然后把改变的代码用类来封装,这样的一种思路也就是我们简单工厂模式的实现方式了。
下面通过一个现实生活中的例子来引出简单工厂模式。
在外面打工的人,免不了要经常在外面吃饭,当然我们也可以自己在家做饭吃,但是自己做饭吃麻烦,因为又要自己买菜,然而,出去吃饭就完全没有这些麻烦的,我们只需要到餐馆点菜就可以了,买菜的事情就交给餐馆做就可以了,这里餐馆就充当简单工厂的角色,下面让我们看看现实生活中的例子用代码是怎样来表现的。
自己做饭的情况:
///自己做饭的情况
///没有简单工厂之前,客户想吃什么菜只能自己炒的
publicclassCustomer
{
///烧菜方法
publicstaticFoodCook(stringtype)
{
Foodfood=null;
//客户A说:
我想吃西红柿炒蛋怎么办?
//客户B说:
那你就自己烧啊
//客户A说:
好吧,那就自己做吧
if(type.Equals("西红柿炒蛋"))
{
food=newTomatoScrambledEggs();
}
//我又想吃土豆肉丝,这个还是得自己做
//我觉得自己做好累哦,如果能有人帮我做就好了?
elseif(type.Equals("土豆肉丝"))
{
food=newShreddedPorkWithPotatoes();
}
returnfood;
}
staticvoidMain(string[]args)
{
//做西红柿炒蛋
Foodfood1=Cook("西红柿炒蛋");
food1.Print();
Foodfood2=Cook("土豆肉丝");
food2.Print();
Console.Read();
}
}
///菜抽象类
publicabstractclassFood
{
//输出点了什么菜
publicabstractvoidPrint();
}
///西红柿炒鸡蛋这道菜
publicclassTomatoScrambledEggs:
Food
{
publicoverridevoidPrint()
{
Console.WriteLine("一份西红柿炒蛋!
");
}
}
///土豆肉丝这道菜
publicclassShreddedPorkWithPotatoes:
Food
{
publicoverridevoidPrint()
{
Console.WriteLine("一份土豆肉丝");
}
}
自己做饭,如果我们想吃别的菜时,此时就需要去买这种菜和洗菜这些繁琐的操作,有了餐馆(也就是简单工厂)之后,我们就可以把这些操作交给餐馆去做,此时消费者(也就是我们)对菜(也就是具体对象)的依赖关系从直接变成的间接的,这样就是实现了面向对象的另一个原则——降低对象之间的耦合度,下面就具体看看有了餐馆之后的实现代码(即简单工厂的实现):
///顾客充当客户端,负责调用简单工厂来生产对象
///即客户点菜,厨师(相当于简单工厂)负责烧菜(生产的对象)
classCustomer
{
staticvoidMain(string[]args)
{
//客户想点一个西红柿炒蛋
Foodfood1=FoodSimpleFactory.CreateFood("西红柿炒蛋");
food1.Print();
//客户想点一个土豆肉丝
Foodfood2=FoodSimpleFactory.CreateFood("土豆肉丝");
food2.Print();
Console.Read();
}
}
///菜抽象类
publicabstractclassFood
{
//输出点了什么菜
publicabstractvoidPrint();
}
///西红柿炒鸡蛋这道菜
publicclassTomatoScrambledEggs:
Food
{
publicoverridevoidPrint()
{
Console.WriteLine("一份西红柿炒蛋!
");
}
}
///土豆肉丝这道菜
publicclassShreddedPorkWithPotatoes:
Food
{
publicoverridevoidPrint()
{
Console.WriteLine("一份土豆肉丝");
}
}
///简单工厂类,负责炒菜
publicclassFoodSimpleFactory
{
publicstaticFoodCreateFood(stringtype)
{
Foodfood=null;
if(type.Equals("土豆肉丝"))
{
food=newShreddedPorkWithPotatoes();
}
elseif(type.Equals("西红柿炒蛋"))
{
food=newTomatoScrambledEggs();
}
returnfood;
}
}
优点与缺点
看完简单工厂模式的实现之后,你和你的小伙伴们肯定会有这样的疑惑(因为我学习的时候也有)——这样我们只是把变化移到了工厂类中罢了,好像没有变化的问题,因为如果客户想吃其他菜时,此时我们还是需要修改工厂类中的方法(也就是多加case语句,没应用简单工厂模式之前,修改的是客户类)。
我首先要说:
你和你的小伙伴很对,这个就是简单工厂模式的缺点所在(这个缺点后面介绍的工厂方法可以很好地解决),然而,简单工厂模式与之前的实现也有它的优点:
∙简单工厂模式解决了客户端直接依赖于具体对象的问题,客户端可以消除直接创建对象的责任,而仅仅是消费产品。
简单工厂模式实现了对责任的分割。
∙简单工厂模式也起到了代码复用的作用,因为之前的实现(自己做饭的情况)中,换了一个人同样要去在自己的类中实现做菜的方法,然后有了简单工厂之后,去餐馆吃饭的所有人都不用那么麻烦了,只需要负责消费就可以了。
此时简单工厂的烧菜方法就让所有客户共用了。
(同时这点也是简单工厂方法的缺点——因为工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都会受到影响,也没什么不好理解的,就如事物都有两面性一样道理)
虽然上面已经介绍了简单工厂模式的缺点,下面还是总结下简单工厂模式的缺点:
∙工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都会受到影响(通俗地意思就是:
一旦餐馆没饭或者关门了,很多不愿意做饭的人就没饭吃了)
∙系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,这样就会造成工厂逻辑过于复杂。
了解了简单工厂模式之后的优缺点之后,我们之后就可以知道简单工厂的应用场景了:
∙当工厂类负责创建的对象比较少时可以考虑使用简单工厂模式()
∙
客户如果只知道传入工厂类的参数,对于如何创建对象的逻辑不关心时可以考虑使用简单工厂模式
(3)工厂方法模式
工厂方法模式之所以可以解决简单工厂的模式,是因为它的实现把具体产品的创建推迟到子类中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式就可以允许系统不修改工厂类逻辑的情况下来添加新产品,这样也就克服了简单工厂模式中缺点。
下面看下工厂模式的具体实现代码(这里还是以简单工厂模式中点菜的例子来实现):
namespace设计模式之工厂方法模式
{
///菜抽象类
publicabstractclassFood
{
//输出点了什么菜
publicabstractvoidPrint();
}
///西红柿炒鸡蛋这道菜
publicclassTomatoScrambledEggs:
Food
{
publicoverridevoidPrint()
{
Console.WriteLine("西红柿炒蛋好了!
");
}
}
///土豆肉丝这道菜
publicclassShreddedPorkWithPotatoes:
Food
{
publicoverridevoidPrint()
{
Console.WriteLine("土豆肉丝好了");
}
}
///抽象工厂类
publicabstractclassCreator
{
//工厂方法
publicabstractFoodCreateFoddFactory();
}
///西红柿炒蛋工厂类
publicclassTomatoScrambledEggsFactory:
Creator
{
///负责创建西红柿炒蛋这道菜
publicoverrideFoodCreateFoddFactory()
{
returnnewTomatoScrambledEggs();
}
}
///土豆肉丝工厂类
publicclassShreddedPorkWithPotatoesFactory:
Creator
{
///负责创建土豆肉丝这道菜
publicoverrideFoodCreateFoddFactory()
{
returnnewShreddedPorkWithPotatoes();
}
}
///客户端调用
classClient
{
staticvoidMain(string[]args)
{
//初始化做菜的两个工厂()
CreatorshreddedPorkWithPotatoesFactory=newShreddedPorkWithPotatoesFactory();
CreatortomatoScrambledEggsFactory=newTomatoScrambledEggsFactory();
//开始做西红柿炒蛋
FoodtomatoScrambleEggs=tomatoScrambledEggsFactory.CreateFoddFactory();
tomatoScrambleEggs.Print();
//开始做土豆肉丝
FoodshreddedPorkWithPotatoes=shreddedPorkWithPotatoesFactory.CreateFoddFactory();
shreddedPorkWithPotatoes.Print();
Console.Read();
}
}
}
使用工厂方法实现的系统,如果系统需要添加新产品时,我们可以利用多态性来完成系统的扩展,对于抽象工厂类和具体工厂中的代码都不需要做任何改动。
例如,我们我们还想点一个“肉末茄子”,此时我们只需要定义一个肉末茄子具体工厂类和肉末茄子类就可以。
而不用像简单工厂模式中那样去修改工厂类中的实现(具体指添加case语句)。
具体代码为:
///肉末茄子这道菜
publicclassMincedMeatEggplant:
Food
{
///重写抽象类中的方法
publicoverridevoidPrint()
{
Console.WriteLine("肉末茄子好了");
}
}
///肉末茄子工厂类,负责创建肉末茄子这道菜
publicclassMincedMeatEggplantFactory:
Creator
{
///负责创建肉末茄子这道菜
publicoverrideFoodCreateFoddFactory()
{
returnnewMincedMeatEggplant();
}
}
///客户端调用
classClient
{
staticvoidMain(string[]args)
{
//如果客户又想点肉末茄子了
//再另外初始化一个肉末茄子工厂
CreatorminceMeatEggplantFactor=newMincedMeatEggplantFactory();
//利用肉末茄子工厂来创建肉末茄子这道菜
FoodminceMeatEggplant=minceMeatEggplantFactor.CreateFoddFactory();
minceMeatEggplant.Print();
Console.Read();
}
}
在工厂方法模式中,工厂类与具体产品类具有平行的等级结构,它们之间是一一对应的。
针对UML图的解释如下:
Creator类:
充当抽象工厂角色,任何具体工厂都必须继承该抽象类
TomatoScrambledEggsFactory和ShreddedPorkWithPotatoesFactory类:
充当具体工厂角色,用来创建具体产品
Food类:
充当抽象产品角色,具体产品的抽象类。
任何具体产品都应该继承该类
TomatoScrambledEggs和ShreddedPorkWithPotatoes类:
充当具体产品角色,实现抽象产品类对定义的抽象方法,由具体工厂类创建,它们之间有一一对应的关系。
(4)抽象工厂模式
下面就以生活中“绝味”连锁店的例子来实现一个抽象工厂模式。
例如,绝味鸭脖想在江西南昌和上海开分店,但是由于当地人的口味不一样,在南昌的所有绝味的东西会做的辣一点,而上海不喜欢吃辣的,所以上海的所有绝味的东西都不会做的像南昌的那样辣,然而这点不同导致南昌绝味工厂和上海的绝味工厂生成所有绝味的产品都不同,也就是某个具体工厂需要负责一系列产品(指的是绝味所有食物)的创建工作,下面就具体看看如何使用抽象工厂模式来实现这种情况。
///下面以绝味鸭脖连锁店为例子演示下抽象工厂模式
///因为每个地方的喜欢的口味不一样,有些地方喜欢辣点的,有些地方喜欢吃不辣点
///客户端调用
6classClient
7{
8staticvoidMain(string[]args)
9{
10//南昌工厂制作南昌的鸭脖和鸭架
11AbstractFactorynanChangFactory=newNanChangFactory();
12YaBonanChangYabo=nanChangFactory.CreateYaBo();
13nanChangYabo.Print();
14YaJiananChangYajia=nanChangFactory.CreateYaJia();
15nanChangYajia.Print();
17//上海工厂制作上海的鸭脖和鸭架
18AbstractFactoryshangHaiFactory=newShangHaiFactory();
19shangHaiFactory.CreateYaBo().Print();
20shangHaiFactory.CreateYaJia().Print();
22Console.Read();
23}
24}
27///抽象工厂类,提供创建两个不同地方的鸭架和鸭脖的接口
29publicabstractclassAbstractFactory
30{
31//抽象工厂提供创建一系列产品的接口,这里作为例子,只给出了绝味中鸭脖和鸭架的创建接口
32publicabstractYaBoCreateYaBo();
33publicabstractYaJiaCreateYaJia();
34}
37///南昌绝味工厂负责制作南昌的鸭脖和鸭架
39publicclassNanChangFactory:
AbstractFactory
40{
41//制作南昌鸭脖
42publicoverrideYaBoCreateYaBo()
43{
44returnnewNanChangYaBo();
45}
46//制作南昌鸭架
47publicoverrideYaJiaCreateYaJia()
48{
49returnnewNanChangYaJia();
50}
51}
54///上海绝味工厂负责制作上海的鸭脖和鸭架
56publicclassShangHaiFactory:
AbstractFactory
57{
58//制作上海鸭脖
59publicoverrideYaBoCreateYaBo()
60{
61returnnewShangHaiYa