中介者模式之我们结婚吧
最后更新于:2022-04-01 06:33:37
很久以前,小小少女情怀的我,怀揣一个美丽的春梦,那就是找一个男孩儿,从心动走到古稀,随着时间的推移,我听到这样的话语,你太天真,可是我仍然愿意相信,冰心对铁凝说的那句话,你不要找,你要等。爱情是美好的,但不是每个人都能收获自己的爱情,别担心,婚庆公司帮你忙,百合网是中国第一家实名制婚恋服务商,以“帮助亿万中国人拥有幸福的婚姻和家庭”为己任。2005年5月,百合网正式发布,并在中国首次推出“心灵匹配,成就幸福婚姻”的独特婚恋服务模式。
2007年3月,百合网成为第一个采用公安部身份认证系统的婚恋网站。百合网率先实行实名制。目前,已有超过7000万注册用户在百合网上寻找他们的终生伴侣。今天我们的设计模式就从婚姻公司开始说起---中介者模式。顾名思义,中介的意思就是指在不同事物或同一事物内部对立两极之间起居间联系作用的环节。官方定义,用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互作用,从而使其耦合松散,而且可以独立地改变她们之间的交互,来看一下中介者模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683744378761.jpg)
依然以我们上述的婚庆公司为例,来探究一下中介者模式是如何应用的,试想一下,在没有婚庆公司之前,大家之间的交流是这个样子的:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_568374438a3e1.jpg)
大家看着是不是有种凌乱的感觉,如果人的数目多起来就会变成网状的结构啦。在我们的软件世界中,原本把一个系统分割成一些对象是可以增强复用性的,但是现在的情况是,这些兑现之间存在着大量的联系,耦合性极高。这是很不利于复用的,同时这种情况使得系统的灵活性大大的降低,使得对系统的扩展很难。如果现在可以借助婚庆公司,那么可以采用另一种方式设计上述的案例,如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683744396ffe.jpg)
看看“网状结构”和“星形结构”的区别,显然采用星形结构就可以避免上面的网状结构存在的问题了,实际上这里的婚庆公司就是指的中介,大家可以通过婚庆公司来进行交流。看一下这个例子的代码结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_56837443a37a7.jpg)
接着来看一下,我们的代码实现:
~~~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Baihe Bh = new Baihe();
Boy c1 = new Boy(Bh);
Girl c2=new Girl (Bh );
Bh.Colleague1 = c1;
Bh.Colleague2 = c2;
c1.Declare("我想找一个女朋友,给她世上最好的温柔!");
c2.Declare("如果明天的路,你不知道该往哪儿走,就留在我身边好不好!");
Console.Read();
}
}
//婚庆公司类,相当于Mediator类
//婚庆公司
abstract class WeddingCompany
{
//声明
public abstract void Declare(string message, People colleague);
}
//人类 相当于Colleague类
abstract class People
{
protected WeddingCompany mediator;
public People (WeddingCompany mediator)
{
this.mediator = mediator;
}
}
//男孩类 相当于ConcreteColleague1类
//男孩
class Boy :People
{
public Boy(WeddingCompany mediator):base( mediator )
{ }
//声明
public void Declare (string message)
{
mediator.Declare(message, this);
}
//获得信息
public void GetMessage(string message)
{
Console.WriteLine("我想找一个女朋友,给她世上最好的温柔");
}
}
//女孩类 相当于ConcreteColleague2类
//女孩
class Girl:People
{
public Girl (WeddingCompany mediator):base(mediator )
{ }
//声明
public void Declare(string message)
{
mediator.Declare(message, this);
}
//获得消息
public void GetMessage(string message)
{
Console.WriteLine("如果明天的路,你不知道往哪儿走,就留在我身边好不好");
}
}
//具体的婚庆公司,百合网这里相当于ConcreteMediator类
//百合网
class Baihe:WeddingCompany
{
private Boy colleague1;
private Girl colleague2;
//男孩
private Boy Colleague1
{ set { colleague1 = value; } }
//女孩
private Girl Colleague2
{ set { colleague2 = value; } }
//声明
public override void Declare(string message, People colleague)
{
if (colleague ==colleague1 )
{
colleague2.GetMessage(message);
}
else
{
colleague1.GetMessage(message);
}
}
}
}
~~~
也许有一天,他真的会出现在我面前,那个在阳光底下微笑的少年,也许有一天,我们会一起去看北极光,我会和他一起仰望星空,谈论当初冰心对铁凝说的话,我不知道直到多久以后的某一天,他真的会出现,但可以确定的是,编程就是生活。
职责链模式之真假美猴王
最后更新于:2022-04-01 06:33:35
孙悟空最终还是没有能逃脱如来的手掌心,因缘注定,皈依佛门,虽没有了做[齐天大圣](http://blog.csdn.net/u010850027/article/details/26497063)是的逍遥快活,也没有大闹天宫时的轰轰烈烈,但是现在有了更重要的一项任务,那就是普度众生,《 西游记》第五十七回,说是“六耳猕猴”化作孙悟空的摸样,伤了唐僧,后又和孙悟空大打出手。。。这位假孙悟空,实力不用多说了吧,和真孙悟空一般无二,大战孙悟空,闹到上天入地下海。在唐僧那:念紧箍咒,两个都喊疼,自然看不出哪个真假;到天宫:拖塔天王拿照妖镜照,也看不出;又到观音那:观音也看不出;最后到幽冥处阎罗那:经“谛听”听过之后,“谛听”却说:“我看出来了,却不敢说”,最后还是如来老佛爷道出六耳真身并用金钵盂罩住,才被孙悟空一棍子打死。这是整个故事,看似很简单,很完整,不过,我发现一个天大的伏笔。
那就是,真假美猴王的故事和我们设计模式中的职责链模式有着异曲同工之妙,在面向对象程式设计里, 职责链模式是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。官方定义---使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理她为止。来看一下职责链模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_568374429a2e1.jpg)
在我们的实际生活中,我们也时常与职责链模式打着交道,比如类似“斗地主”这样的游戏中,小北出牌给他的下家,下家看看手中的牌,如果要不起上家的牌则将出牌请求再转发给他的下家,其下家再进行判断。一个循环下来,如果其他人都要不起该牌,则小北可以打出新的牌。在这个过程中,牌作为一个请求沿着一条链在传递,每一位纸牌的玩家都可以处理该请求。以我们上述真假美猴王的故事为例,看看我们的代码实现:
~~~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
{
TangSeng = new TangSeng("唐僧");
LiTianWang = new LiTianWang("李天王");
GuangYin = new GuangYin("观音菩萨");
DiTing = new DiTing("谛听");
RuLai = new RuLai("如来");
}
//唐僧-->托塔李天王-->观音菩萨-->阎罗殿谛听-->如来佛祖
judgement.TangSeng.NextImmortal = judgement.LiTianWang;
judgement.LiTianWang.NextImmortal = judgement.GuanYin;
judgement.TangSeng.NextImmortal = judgement.DiTing;
judgement.DiTing.NextImmortal = judgement.RuLai;
//两美猴王入场
string SunWuKong = "SunWuKong",
SixEarMonkey = "SixEarMonkey";
//开始判断真假美猴王
TheRealMonkeyKing RealMonkeyKing = judgement.Judge(SunWuKong, SixEarMonkey);
Console.WriteLine();
Console.WriteLine("Then,真正的美猴王是:" + RealMonkeyKing + "孙悟空");
Console.ReadKey();
}
}
//管理者,也就是众多神仙们
abstract class Immortal
{
protected string name;
//神仙的上级,这里的级别并没有严格的等级制度之分,只是因为该级别身份的神仙没有辨别出真假孙悟空
protected Immortal superior;
public Immortal (string name )
{
this.name = name;
}
//设置神仙的上级,这里的级别并没有严格的等级制度之分,只是因为该级别身份的神仙没有辨别出真假孙悟空
public void SetSuperior(Immortal superior)
{
this.superior = superior;
}
//申请请求
abstract public void RequestApplication(Request request);
}
//接下来,具体的神仙判断,首先是唐僧
//唐僧
public class TangSeng : Immortal
{
/// 唐僧判断真假美猴王
public TheRealMonkeyKing Handler(string SunWukong, string SixEarMonkey)
{
Console.WriteLine(" 唐僧念《紧箍儿咒》,二人一齐叫苦, 唐僧也不认得真假,两悟空打到李天王哪里");
return NextImmortal.Immortal(SunWukong, SixEarMonkey);
}
// 指定下一路神仙 来判断真假美猴王
public Immortal NextImmortal { get; set; }
}
//托塔李天王
public class LiTianWang : Immortal
{
//李天王判断真假美猴王
public TheRealMonkeyKing Immortal(string SunWukong, string SixEarMonkey)
{
Console.WriteLine(" 李天王取照妖镜照住,镜中乃是两个孙悟空,毫发不差。玉帝亦辨不出,两悟空大战几百回合,来到了观音那里");
return NextImmortal.Immortal(SunWukong, SixEarMonkey);
}
// 指定下一路神仙 来判断真假美猴王
public Immortal NextImmortal { get; set; }
}
//观音
public class Guanyin : Immortal
{
//观音判断真假美猴王
public TheRealMonkeyKing Immortal(string SunWukong, string SixEarMonkey)
{
Console.WriteLine(" 观音念《紧箍儿咒》,二人一齐叫苦, 唐僧也不认得真假,两悟空打到阎罗殿,谛听出来辨别");
return NextImmortal.Immortal(SunWukong, SixEarMonkey);
}
// 指定下一路神仙来判断真假美猴王
public Immortal NextImmotal { get; set; }
}
//谛听
public class DiTing :Immortal
{
// 谛听判断真假美猴王
public TheRealMonkeyKing Immortal(string SunWukong, string SixEarMonkey)
{
Console.WriteLine("阎罗殿的谛听可以分辨的出真假, 却不敢说出来,因为六耳猕猴的后台很强:如来是也");
return NextImmortal.Immortal(SunWukong, SixEarMonkey);
}
// 指定下一路神仙 来判断真假美猴王
public IHandler NextHandler { get; set; }
}
//如来
public class RuLai :Immortal
{
// 如来判断真假美猴王
public TheRealMonkeyKing Immortal(string SunWukong, string SixEarMonkey)
{
Console.WriteLine(" 如来佛辨出真假,是所有神仙都没有听说过的新物种:六耳猕猴");
return TheRealMonkeyKing.SunWukong;
}
}
~~~
对于真假美猴王这块的内容,大家争论不一,在真假美猴王事出之前,孙悟空并不完全都听唐僧的话,甚至有时候,还闹个小矛盾,导致唐僧只好念紧箍咒,典型的一个叛逆者形象。可自从真假美猴王事出之后,孙悟空从此保护唐僧安安分分。而以此事可以看出,孙悟空前后可判若两人。不排除,孙悟空已被如来利用六耳猕猴一战中,安安静静、无人知晓的收服了。也有人说佛祖能通晓过去未来,早就设此考验,算到六耳猕猴必死于孙悟空手上,届时孙悟空就能战胜心魔,真正走上成仙之路。如果佛祖出手阻挡孙悟空的那一棍,保住六耳猕猴性命,那么设此考验又有何意义?这也说明了,孙悟空杀死六耳猕猴,彻底清除了自己内心的邪恶一面,从内心上懂得了成仙成佛的真正的道路。所以在“真假美猴王”一难之后才会服服帖帖,真心为皈依佛门而保护唐僧。
西游记的故事还在演绎流传,设计模式的精彩稍后继续......
命令模式之做我的齐天大圣还是奉旨上界
最后更新于:2022-04-01 06:33:32
> 话说,《西游记》中有这样一段故事,玉帝命令太白金星召美猴王上天:"金星径入水帘洞当中,面南立定道:我是西方太白金星,奉玉帝招安圣旨,请你上界报道,拜受仙录。具体对话内容如下所示:
> 太白金星:大圣,我主玉帝听说你才能出众,对你非常期待啊。
> 孙大圣:是么,真的?
> 太白金星:上界众仙也很仰慕大圣你啊。
> 孙大圣:这......
> 太白金星:大圣还犹豫是么?
> 孙大圣:我去做什么?
> 太白金星:绝对一把手,你的地盘好像很大,具体上去就知道了。
> 孙大圣:那我去看看也好。
> 太白金星:哈哈,大圣,请!
今天我们的设计模式就从孙大圣上界报道开始说起---命令模式,即将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,实现二者之间的松耦合。
命令模式属于行为模式。意图是将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求的排队或记录请求的日志,以及支持可以撤销的操作。又叫动作(Action)或者事务(Transaction)。有时必须向一个对象提交请求,但是并不知道关于被请求的操作或者请求的接受者的任何信息。命令模式通过将请求本身变成一个对象来使工具箱对象可向未指定的应用对象提出请求,这个对象可以被存储并像其他的对象一样被传递,这一个模式的关键是抽象的Command类,它定义了一个执行操作的接口,其最简单的形式是一个抽象的Execute操作,具体的Command子类将接收者作为其一个实例变量,并实现Execute操作,制定接收者采取的动作,而接收者有执行该请求所需的具体信息。这就是命令模式(Command Pattern)首先来看一下命令模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_568374417875a.jpg)
来分析一下,孙悟空上界报道中的逻辑关系,'"玉帝是系统的客户端,太白金星是命令的发出者,猴王是命令的接收者,圣旨就是命令。玉帝的这一道命令就是要求猴王到上界报到。玉帝只管发出命令,而不管命令是怎样传达到美猴王的。太白金星负责将圣旨传到,可是美猴王怎么执行圣旨、何时执行圣旨是美猴王自己的事。来看一下代码实现:
~~~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ImperialEdict i = new ImperialEdict();
Command c = new ReportCommand();
Venus v = new Venus();
v.ObtainOrder();
v.ExcuteCommand();
Console.Read();
}
}
//抽象命令类
//抽象命令
public abstract class Command
{
protected Imperial receiver;
public Command(Imperial receiver)
{
this.receiver = receiver;
}
//执行命令
abstract public void ExcuteCommand();
}
//具体命令类
//上界报到命令
class ReportCommand : Imperial
{
public ReportCommand(Imperial receiver)
:base(receiver)
{ }
public override void ExcuteCommand()
{
receiver.Report();
}
}
//太白金星
public class Venus
{
private Imperial command;
//收到命令
public void ObtainOrder(Imperial command)
{
this.command =command ;
}
//执行命令
public void Notify()
{
command.ExcuteCommand ();
}
}
//猴王
public class MonkeyKing
{
//上界报道
public void Imperial()
{
Console .WriteLine ("上界报道!");
}
}
}
~~~
对于命令模式,我们可以这样理解,提供一个抽象的Command接口,将执行命令操作的方法封装到Command类接口中,ConcreteCommand实现这个Command接口方法,通过调用Receiver实例变量处理请求。客户端定义一个Invoker对象存储该concreteCommand对象,该invoker通过调用command对象的递交一个请求。
桥接模式之牛郎织女幸福牵手
最后更新于:2022-04-01 06:33:30
这是一个很美丽的,千古流传的爱情故事,每到农历七月初七,相传牛郎织女鹊桥相会的日子,牛郎和织女被银河隔开,只允许每年的农历七月七日相见。为了让牛郎和织女能顺利相会。各地的喜鹊就会飞过来用大家的身体紧贴着搭成一座桥,此桥就叫做鹊桥。牛郎和织女便在这鹊桥上相会。我就会抬头仰望星空,寻找银河两边的牛郎星和织女星,希望能看到他们一年一度的相会......
在牛郎织女相会的过程中,喜鹊是功不可没的大臣,我们今天的设计模式就从鹊桥开始说起---桥接模式,顾名思义,在接口与实现之间,架设一座桥梁,这座桥,其实并非是为了过河,也不是为了让牛郎织女相会,那是人家喜鹊的事儿,咱要符合单一职责原则,干好自己的事儿,那就是将抽象部分与她的实现部分分离,使她都可以独立地变化。首先来看一下我们桥接模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_568374404f6fc.jpg)
为了更好的理解桥接模式,我们再来举一个例子,走进南四楼的机房,你除了看到一个个认真敲代码的孩子之外,还会看到大家使用的电脑,有惠普,联想,操作系统有Win7、Win8,由此我们可以画一个类图,如下所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_568374406028c.jpg)
全国人民都知道,电脑的品牌除了惠普和联想,还有戴尔、宏碁、华硕等;操作系统除了Win7和Win8之外,还有XP、Linux,如此一来,上面的结构图,似乎美中有着不足,改善一下我们的类图,如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_568374406d4b1.jpg)
我们来分析一下上面的两张图,可以发现一个是用继承完成的,一种是用组合/聚合的方式完成的,而采用组合/聚合的方式就是所谓的抽象与实现分离。实际上在设计类时,我们应该首先考虑的是组合/聚合的方式,而不是考虑继承的方式,因为继承是一种强耦合关系,使用继承使得子类过多的依靠父类,接着,我们来看一下代码实现:
~~~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ComputerBrand ss;
ss = new ComputerBrandH();
ss.SetComputerOperatingSystem(new Win7() );
ss.Run ();
ss.SetComputerOperatingSystem(new Win8 ());
ss.Run ();
ss = new ComputerBrandL();
ss.SetComputerOperatingSystem(new Win7() );
ss.Run ();
ss.SetComputerOperatingSystem(new Win8() );
ss.Run ();
Console .Read ();
}
}
//电脑的操作系统系统
abstract class OperatingSystem
{
public abstract void Run();
}
//Win7.Win8系统等具体类
//Win7操作系统
class Win7:OperatingSystem
{
public override void Run()
{
Console.WriteLine("运行Win7系统");
}
}
//Win8操作系统
class Win8:OperatingSystem
{
public override void Run()
{
Console.WriteLine("运行Win8系统");
}
}
//电脑品牌类
//电脑品牌
abstract class ComputerBrand
{
protected OperatingSystem OperatingSystem;
//设置电脑系统
public void SetComputerOperatingSystem (OperatingSystem OperatingSystem)
{
this.OperatingSystem = OperatingSystem;
}
//运行
public abstract void Run();
}
//惠普和联想具体类
//惠普
class ComputerBrandH:ComputerBrand
{
public override void Run()
{
OperatingSystem.Run();
}
}
//联想
class ComputerBrandL:ComputerBrand
{
public override void Run()
{
OperatingSystem.Run();
}
}
}
~~~
在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?这个时候桥接模式就应用而生。牛郎织女通过鹊桥幸福牵手,而我们的桥接模式也在她的一方土地,起着她独特的作用......
单例模式之简约美
最后更新于:2022-04-01 06:33:28
**生活中的单例**
中国(China),位于东亚,是一个以华夏文明为主体、中华文化为基础,以汉族为主要民族的统一多民族国家,通用汉语。中国疆域内的各个民族统称为中华民族,龙是中华民族的象征。古老的中国凭借自身的发展依旧美丽的屹立于东方民族之林,闪耀着她动人的光彩,世界上只有一个中国,任何部分都是祖国不可分割的一部分,今天我们的设计模式就从伟大的祖国开始说起---单例模式。
**详解单例模式**
单例模式是什么?跟我们的祖国有着怎样的关系呢?首先我们来看一下单例,从“单例”字面意思上理解为—一个类只有一个实例,所以单例模式也就是保证一个类只有一个实例的一种实现方法。官方定义:确保一个类只有一个实例,并提供一个全局访问点。在学习的过程中,我们需要把握三个主要的关键点,一、某个类只能有一个实例;二、它必须自行创建这个实例;三、它必须自行向整个系统提供这个实例。来看一下单例模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743f9d859.jpg)
**实现方法**
一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
**代码实现**
**第一版(基本代码)**
~~~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace 单例模式基本模型
{
class Program
{
//客户端
static void Main(string[] args)
{
Singleton Instance1 = Singleton.GetInstance();
Singleton Instance2 = Singleton.GetInstance();
if (Instance1 == Instance2)
{
Console.WriteLine("两个实例是一模一样的实例。");
}
}
}
class Singleton
{
private static Singleton instance;
//构造方法采用private,外界便无法用new创建该类的实例
private Singleton()
{ }
//该方法提供一个获得该类实例的全局访问点,是唯一的
public static Singleton GetInstance()
{
//如果实例不存在,则返回一个新实例,否则返回原实例。
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
~~~
但是上述代码存在一些缺点,线程不安全,多线程情况下,多个线程同时访问Singleton,调用GetInstance()方法,同时判断instance==null,得到真值,导致创建多个实例,这不符合单例模式的基本原则。那我们要怎么办捏,为了解决以上缺点,我们来看改进的代码(一下版本的)
**第二版(多线程时的单例)**
~~~
class Singleton
{
private static Singleton instance=null;
//创建一个静态只读的进程辅助对象
private static readonly object ProgressLock = new object();
//构造方法采用private,外界便无法用new创建该类的实例
private Singleton()
{ }
//该方法提供一个获得该类实例的全局访问点,是唯一的
public static Singleton GetInstance()
{
lock (ProgressLock)
{
if (instance == null)
{
instance = new Singleton();
}
}
return instance;
}
}
~~~
每次调用GetInstance()方法都需要lock,这种做法是会影响性能的,所以我们需要对这个类进行改良。
**第三版(双重锁定)**
~~~
class Singleton
{
private static Singleton instance=null;
//创建一个静态只读的进程辅助对象
private static readonly object ProgressLock = new object();
//构造方法采用private,外界便无法用new创建该类的实例
private Singleton()
{ }
//该方法提供一个获得该类实例的全局访问点,是唯一的
public static Singleton GetInstance()
{
if (instance == null)
{
lock (ProgressLock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
~~~
上述构造方式只有在实例未被创建的时候才加锁,避免了每次调用GetInstance()方法都加锁损失性能的问题。但是相对于后面的做法,她仍然有着美中不足的地方。
**第四版(静态初始化)**
~~~
//sealed关键字防止派生
public sealed class Singleton
{
//在第一次引用类的成员时创建实例,公共语言运行库负责处理变量的初始化
private static readonly Singleton instance=new Singleton();
//构造方法采用private,外界便无法用new创建该类的实例
private Singleton()
{ }
public static Singleton GetInstance()
{
return instance;
}
}
~~~
这种方式是在自己被加载时就将自己实例化,称为饿汉式。由于有.NetFramework 进行初始化,所以我们对实例化机制的控制权较少,没办法和其他实现一样实现延迟初始化。在上面三种形式中,您能够在实例化之前使用非默认的构造函数或执行其他任务。
**第五版(完全延迟加载实例化)**
~~~
public sealed class Singleton
{
private Singleton()
{
}
public static Singleton GetInstance()
{
return Nested.instance;
}
class Nested
{
static Nested()
{
}
internal static readonly Singleton instance = new Singleton();
}
}
~~~
**写在后面的话**
前三版编程方法,因为会面临多线程访问安全的问题,需要做双重锁定这样的处理才可以保证安全,但能够在实例化之前使用非默认的构造函数或执行其他任务,第四版(静态初始化方式)是类一加载就实例化的对象,占用系统资源,所以到底实用哪一种方式,视具体情况而定.
迭代器模式之看芒果台还是央视nie?
最后更新于:2022-04-01 06:33:25
”大风车吱呀吱悠悠的转,这里的风景啊真好看,天好看,地好看,还有一群快乐的小伙伴,大风车转呀转悠悠,快乐的伙伴手牵着手,牵着你的手,牵着我的手......“童年的美好时光因为有了《动画城》的陪伴而倍感幸福,那个时候,家里有一台古董式的电视机,是一台黑白电视机,转台的时候需要通过电视机上面的那些按钮来完成,也就是换台的按钮,但是并不是每次都那么幸运,可以准时收看我喜欢的儿童节目,有时候出现一片白茫茫的雪花点儿,剩下的就只是郁闷了。就这样,动画片稳稳的托住了我童年的欢乐。
随着科技的飞速发展,各种电视机可谓是百家争鸣,百花齐放,三星SAMSUNG ,索尼SONY液晶电视 ,海信Hisense液晶电视等,让你眼花缭乱,而那些黑白的电视机慢慢淡出我们的视线,归隐山林,与那时的电视机相比,现今的电视机给我们带来的最大便利之一就是增加了电视机遥控器,我们在进行开机、关机、换台、改变音量等操作时都无须直接操作电视机,可以通过遥控器来间接实现。我们可以将电视机看成一个存储电视频道的集合对象,通过遥控器可以对电视机中的电视频道集合进行操作,如返回上一个频道、跳转到下一个频道或者跳转至指定的频道。遥控器为我们操作电视频道带来很大的方便,用户并不需要知道这些频道到底如何存储在电视机中。
今天我们的设计模式就从电视机遥控器开始说起,在软件开发中,也存在大量类似电视机一样的类,它们可以存储多个成员对象,这些类通常称为聚合类(Aggregate Classes),对应的对象称为聚合对象。为了更加方便地操作这些聚合对象,同时可以很灵活地为聚合对象增加不同的遍历方法,我们也需要类似电视机遥控器一样的角色,可以访问一个聚合对象中的元素但又不需要暴露它的内部结构。首先我们来看一下迭代器模式的结构图;
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743e95c3b.jpg)
对上述结构图进行一些解释:
Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法,例如:用于获取第一个元素的first()方法,用于访问下一个元素的next()方法,用于判断是否还有下一个元素的hasNext()方法,用于获取当前元素的currentItem()方法等,在具体迭代器中将实现这些方法。
ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数。
Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
ConcreteAggregate(具体聚合类):它实现了在抽象聚合类中声明的createIterator()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。
在迭代器模式中,提供了一个外部的迭代器来对聚合对象进行访问和遍历,迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过而哪些没有。迭代器的引入,将使得对一个复杂聚合对象的操作变得简单。
迭代器模式遍历集合的成熟模式,迭代器模式的关键是将遍历集合的任务交给一个叫做迭代器的对象,它的工作时遍历并选择序列中的对象,而客户端程序员不必知道或关心该集合序列底层的结构。
组合模式之拼凑的美好
最后更新于:2022-04-01 06:33:23
廊坊的风一如既往的在窗外刮着,天地间肆意地飘洒,纵情在一刹那,为何现在只剩下风吹乱我的发,乱蓬蓬的,还是去超市逛逛吧,买吃的`(*∩_∩*)′,走进华联超市,热情的店员招呼着我,开始为我介绍,推荐各种各样商品,店员向我推荐了他们的会员卡,全场所有项目均八折,每逢节假日打五折,我心想那太划算了,而且他们总店,分店,加盟店都可以用,所以就办了张会员卡。今天我们的设计模式就从超市会员卡开始说起。
这个俨然就是我们设计模式中的组合模式----组合模式有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。看下面的两幅图片:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743dcbed6.jpg) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743ddb91a.jpg)
上面两幅图片我们可以看做是一个文件结构,对于这样的结构我们称之为树形结构。在《大话设计模式》中我们了解到可以通过调用某个方法来遍历整个树,当我们找到某个叶子节点后,就可以对叶子节点进行相关的操作。我们可以将这颗树理解成一个大的容器,容器里面包含很多的成员对象,这些成员对象即可是容器对象也可以是叶子对象。但是由于容器对象和叶子对象在功能上面的区别,使得我们在使用的过程中必须要区分容器对象和叶子对象,但是这样就会给客户带来不必要的麻烦,对于客户而言,始终希望能够一致的对待容器对象和叶子对象。这就是组合模式的设计动机:组合模式定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程中无须进行区分,可以对他们进行一致的处理。下面一起来看一下组合模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743dec640.jpg)
以上述在华联超市办会员卡为例,看看组合模式是如何在代码中实现:
~~~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
StoreOrBranch store = new StoreOrBranch("北京总店");
StoreOrBranch brach = new StoreOrBranch("廊坊分店");
JoinInStore jstore = new JoinInStore("安徽加盟店一");
JoinInStore jstore1 = new JoinInStore("海南加盟店二");
brach.Add(jstore);
brach.Add(jstore1);
store.Add(brach);
store.PayByCard();
}
}
///
/// 店面类 抽象出来的店面部件
///
public abstract class Storefront
{
//店名
protected string storeName = string.Empty;
public string StoreName
{
get
{
return storeName;
}
}
//添加店面
public abstract void Add(Storefront store);
//删除店面
public abstract void Remove(Storefront store);
//定义所有部件公用的行为 刷卡行为
public abstract void PayByCard();
}
public class StoreOrBranch : Storefront
{
//构造函数
public StoreOrBranch() { }
public StoreOrBranch(string storeName)
{
this.storeName = storeName;
}
List myStoreList = new List();
//刷卡消费
public override void PayByCard()
{
Console.WriteLine("店面{0}的积分已累加进该会员卡", storeName);
foreach (Storefront sf in myStoreList)
{
sf.PayByCard();
}
}
//增加店面
public override void Add(Storefront store)
{
myStoreList.Add(store);
}
//解除店面
public override void Remove(Storefront store)
{
myStoreList.Remove(store);
}
}
public class JoinInStore : Storefront
{
//构造函数
public JoinInStore() { }
public JoinInStore(string storeName)
{
this.storeName = storeName;
}
//刷卡消费
public override void PayByCard()
{
Console.WriteLine("店面{0}的积分已累加进该会员卡", storeName);
}
public override void Add(Storefront store)
{ }
public override void Remove(Storefront store)
{ }
}
}
~~~
组合模式的目的是:让客户端不再区分操作的是组合对象还是叶子对象,而是以一个统一的方式来操作。实现这个目标的关键之处,是设计一个抽象的组件类,让它可以代表组合对象和叶子对象。这样一来,客户端就不用区分到底是组合对象还是叶子对象了,只需要全部当成组件对象进行统一的操作就可以了。
备忘录模式之备份是一种态度
最后更新于:2022-04-01 06:33:21
有些事,错误了就是永远;有些人,一转身就是一辈子,不要轻易忽略任何爱,哪怕只是前天母亲节一通平凡的电话;时光走着她的路,路过青春的脚步,每个人或许都有遗憾的时候,总是要等到睡觉前,才知道功课只做了一点点总是要等到考试后,才知道该念的书都没有念,为了让自己的人生少些许遗憾,需要我们做好时间管理,提前做好准备,认真思量之后再做打算。
但是在神奇的编程世界中,有一种魔法,可以让你避免很多遗憾,强大的word中具有“Ctrl+Z”的功能,PS等一些软件中,也具有此类功能,基本上所有的带编辑功能的软件都具有撤销这个功能,撤销功能给了我们恢复先前状态的机会。拿我们电脑系统而言,可以进行备份,把系统打成压缩包就是备份,为了在系统出现崩溃情况或中毒时还原成以前的样子!今天我们的模式就从备份开始说起,备忘录模式----在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。一如她自己的名字,就是为了备忘,首先我们来看一下备忘录模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743d22a02.jpg)
刚开始接触的电脑的时候,首先需要掌握的就是学会装系统,装来装去,免不了有点儿麻烦,这个时候,有了新的需求,就有了新的解决方案,那就是对我们先前的电脑进行备份,有句话这样说,备份是一种态度, 以电脑备份为例,看看备忘录模式如何在实际中运用:
发起人(Originator)类
~~~
public class WindowsSystem
{
private string status;
//需要保存的属性
public string Status
{
get
{
return status;
}
set
{
status = value;
}
}
public Memento createOtherSystem() //创建备忘录
{
return new Memento(status);
}
public void recoverSystem(Memento m) //恢复备忘录
{
this.status = m.Status;
}
}
~~~
备忘录(Memento)类
~~~
public class Memento
{
private string status;
public string Status
{
get
{
return status;
}
set
{
Console.WriteLine("系统处于:" + this.status);
status = value;
}
}
public Memento(string status)
{
this.status = status;
}
}
~~~
管理者(Caretaker)类
~~~
public class UserComputer
{
private Memento memento;
public Memento recoverMemento()
{
// 恢复系统
return this.memento;
}
public void createMemento(Memento memento)
{
// 保存系统
this.memento = memento;
}
}
~~~
客户端程序
~~~
class Program
{
static void Main(string[] args)
{
WindowsSystem Win8 = new WindowsSystem(); // Win8系统
UserComputer user = new UserComputer();
Win8.Status = "良好的状态"; // Win8处于良的运行状态
user.createMemento(Win8.createOtherSystem()); // 用户进行备份,Win8系统产生备份文件
Win8.Status = "状态较差"; // Win8处于较差的状态
Win8.recoverSystem(user.recoverMemento()); // 用户发出命令,进行备份还原
11. Console.WriteLine("当前系统处于" + Win8.Status);
Console.ReadKey();
}
}
~~~
在软件开发中,有时需要保存一个对象的状态,以便于允许用户取消相关操作或者从以往的状态中恢复过来。比如一个文档版本管理系统,可以根据需要将指定文档恢复到之前保存过的任意一个状态。这时就可以通过备忘录模式来实现。备忘录模式在我们生活中的应用还有很多,比如象棋中的悔棋功能,游戏中的存档功能,都是备忘录在实际生活中的应用。
用好备忘录,让我们的人生中少些许遗憾和后悔,多一份精彩,多一份期待......
适配器模式之合适的才是最好的
最后更新于:2022-04-01 06:33:19
曾经有一副小米耳机摆在我的面前,但是我没有去好好珍惜,直到她掉到我的水杯里我才后悔莫及,尘世间最痛苦的事莫过于此,如果上天再给我一次机会,我会对你说:我应该买个防水的耳机。话说昨天晚上回到宿舍,禁不住美食的诱惑,大晚上的,咱不能吃,看看也行啊,于是,百度搜索“舌尖上的中国”,看看看着,感觉口渴,于是蹭蹭蹭赶紧找水喝,喝水的时候还不忘把手机拿在手里,耳机插在耳朵里,说时迟,那时快,我的耳机凉快的洗了个澡,可能是因为天气比较热,耳机也想凉快凉快。
我去市面上没有找到2.5的耳机,基本上是3.5接口,没办法我只好买了个3.5接口的耳机,老板告诉你:“我给你一个适配器”这不问题就解决了。3.5的接口的耳机在我手机上本来是没法使用的,因为它没有按照2.5接口的设计,而现在我又想使用这幅耳机,于是乎有了“适配器(Adapter)”这个一个东西出来了。Adapter模式的定义:把一个类的接口变换成客户端所期待的另外一种接口,使得原本由于接口不兼容而不能再一起工作的那些类可以一起工作。我的耳机插口是2,5的,倒霉熊孩子,买错了,买了一个3.5的,怎么办呢?这个时候需要一个“适配器”来帮助我解决问题。今天的设计模式就从我们的耳机开始说起---适配器模式。首先来看看我们适配器的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743c670df.jpg)
对上面的类图进行一些阐述:
目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
需要适配的类(Adaptee):需要适配的类或适配者类。
适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。
接下来,以上面的例子,看看我们的代码实现:
~~~
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Target target = new Adapter();
target.Provide2_5();
Console.Read();
}
}
//Targe 这里相当于2.5插孔的耳机
class Target
{
public virtual void Provide2_5()
{
Console.WriteLine("我是2.5的接口");
}
}
//Adaptee 这里相当于3.5插孔的耳机
class Adaptee
{
public virtual void Provide3_5()
{
Console.WriteLine("我是3.5的接口");
}
}
//Adapter 这里相当于转换器
class Adapter:Target
{
private Adaptee adaptee = new Adaptee();
public override void Provide2_5()
{
adaptee.Provide3_5();
}
}
}
~~~
编程如人生,合适的才是最好的,鹰击长空,鱼翔潜底,大自然因它们而变得丰富多彩,只因它们找到了适合自己的位置。人类也是如此,找到属于自己的位置,你的人生才会充满意义。
状态模式之看尽人生百态
最后更新于:2022-04-01 06:33:16
有人说“人的起点起初都是一样的,在后来的加加减减中,慢慢出现了差距……”人生是什么?人生如你脚下的路,如但丁在《神曲》里的一句话“在人生旅程的中途……”不论曾经做对了什么,做错了什么,用一颗心、一生情去坚定走你没走完的路。人生是什么?是一幅优美的画卷,是一段离奇的爱情故事,还是一曲悠悠的伤感乐曲……我无法形容人生是什么,为什么会有各式各样的人生经历,有人哭有人笑,有人淡然,有人超然,细看人间故事,品酸甜苦辣。
每个人的人生犹如一个个小小的圆圈,随着时间的推移,这个圆慢慢的慢慢的长大,圆里面是一个过程,纷繁复杂着那一段一段的经历。从童年开始,一直到老年落幕,我们都一直在人生的舞台上尽情演绎自己的故事。今天我们的设计模式就从人生开始说起,人生有着五个明确的阶段,从童年,少年,青年,中年,一直到老年落幕,也可以这么说,人的一生分为五个状态,宛如我们设计模式中的状态模式,首先我们来看一下状态模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743be27b9.jpg)
以我们的人生为例,看看状态模式是如何活跃着我们人生的舞台:
首先抽象状态类-----抽象类 LifeState
~~~
//定义一个抽象的LifeState类
public abstract class LifeState
{
public abstract void ChangeLife(Life l);
}
~~~
具体的人生阶段
~~~
//童年阶段-ChildhoodState
class ChildhoodState: LifeState
{
public override void ChangeLife(Life l)
{
Console.WriteLine("童年是一片无边无际的蓝天,包纳了无限的景象!");
l.State= new JuvenileState();
}
}
//少年阶段-JuvenileState
class JuvenileState :LifeState
{
public override void ChangeLife(Life l)
{
Console .WriteLine("少年要用笑脸去追寻梦想,面对梦想,迎接梦想!");
l.State= new YouthState();
}
}
//青年阶段- YouthState
class YouthState :LifeState
{
public override void ChangeLife(Life l)
{
Console.WriteLine("美丽的豆蔻年华,从青涩走向成熟!");
l.State = new MidlifeState ();
}
}
//中年阶段-MidlifeState
class MidlifeState :LifeState
{
public override void ChangeLife(Life l)
{
Console.WriteLine("中年的你,是否实现自己的愿望,满意的工作,幸福的家庭!");
l.State= new ElderyState();
}
}
//老年阶段-ElderyState
class ElderyState:LifeState
{
public override void ChangeLife(Life l)
{
Console .WriteLine ("最美不过夕阳红!");
}
}
~~~
人生类-----Life
~~~
//定义一个人生类-Life
public class Life
{
//初始化当前人生
private LifeState state;
public Life(LifeState state)
{
this.state = state;
}
public LifeState State
{
get { return state; }
set { state = value; }
}
public void ChangeLife() //方法名可以改变
{
state.ChangeLife(this);
}
}
~~~
客户端代码:
~~~
static void Main(string[] args)
{
Life life = new Life( new ChildhoodState());
life.ChangeLife();
life.ChangeLife();
life.ChangeLife();
life.ChangeLife();
life.ChangeLife();
Console.Read();
}
~~~
运行结果如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743c026da.jpg)
状态模式主要解决的是当控制一个对象状态转化的条件表达式过于复杂时的情况,把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化,总的来说就是当一个对象的内在状态改变时允许改变其行为,这个对象看起来是改变了其类。
在面向对象软件设计时,常常碰到某一个对象由于状态的不同而有不同的行为。如果用if else或是switch case等方法处理,对象操作及对象的状态就耦合在一起,碰到复杂的情况就会造成代码结构的混乱。在这种情况下,就可以使用状态模式来解决问题。
大话设计模式之三世同堂
最后更新于:2022-04-01 06:33:14
工厂,又称制造厂,是一类用以生产货物的大型工业建筑物。大部分工厂都拥有以大型机器或设备构成的生产线。在世界近代史中泛指资本主义机器大生产,即使用机械化劳动代替手工劳动的资本主义工业场所。在我们设计模式中也生活着这么一家人,他们分别是简单工厂模式,工厂方法模式和抽象工厂模式,他们都是属于创建型设计模式,这三种创建型模式都不需要知道具体类。我们掌握一种思想,就是在创建一个对象时,需要把容易发生变化的地方给封装起来,来控制变化(哪里变化,封装哪里),以适应客户的变动,项目的扩展。这三种模式按照可维护可扩展的特点来看,工厂方法模式是简单工厂模式的进化,而抽象工厂模式,又是工厂方法模式的进化。
举个简单的例子,春天是播种的季节,我想此刻在我的家-美丽的遥远的安徽,爷爷奶奶正在菜园子里忙着种植蔬菜苗,如果,奶奶只想让菜园单纯的只要种上蔬菜就可以了,那么就用简单工厂,奶奶养育了四个儿女,大过年的一大家子人回来,奶奶总想让那方团圆桌摆上各式各样的菜肴,这个时候就需要用工厂方法才可以实现,就是把共有的东西抽象出来,菜的种类多了,需要的土地规模就会扩大,如果要扩大菜园子的规模,比如一个在村头,一个在村尾,这样工厂方法就无法实现了,就应当用抽象工厂,各种各样的蔬菜,又组成一个菜园子。接下来,依次来讲解一下,三个工厂,依然以我们上述例子:
首先简单工厂模式, 我们把简单工厂模式比喻成菜园子,菜园子有他自己的属性方法,他封转了浇水,施肥,锄草三个方法,要求类是客户端,她只需要调用农活类和菜园子类就可以完成任务,而不需要 一次调用浇水,施肥,锄草所以,实现了封装。一起来看一下她的类图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743b13eb9.jpg)
如果菜园子中蔬菜的种类越来越多,比如过长豆角,黄瓜,葫芦等这些蔬菜属于攀藤类蔬菜,需要支撑架来支撑她们来进行持续生长,茄子,茼蒿这些蔬菜需要定时削剪叉枝,以保持主干支的养分,这个时候,我们就需要修改代码,首先,我们需要增加支撑架,削剪的类,然后,在菜园子类中改变select或者是if条件分支语句,这样,不仅增加了类,还需要修改原有的类,这样一来,违背了开放-封闭原则,这也就是为什么简单工厂模式不能列入GOF的23中模式的原因之一。
而此时,我们需要把简单工厂模式中的“菜园子”类加以改进,让她符合开放-封闭原则,也就是取出她的select case选择分支语句,让在增加方法的时候只需要添加类,这样便符合开闭原则,而简单工厂和工程方法的区别,也就在此,看看我们的类图是如何实现的。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743b207b1.jpg)
我们一直都在写一个菜园子的农作物,但是,如果需要写村头和村尾两个菜园子的农作物,而且我们可以随时切换知道两个菜园子的农作物情况,那么,我们就应该考虑扩充工厂方法模式,而需求不断演化的过程中,我们就重构了一个非常重要的设计模式:抽象工厂模式,类图如下:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743b2ea2e.jpg)
无论是简单工厂模式、工厂模式还是抽象工厂模式,它们本质上都是将不变的部分提取出来,将可变的部分留作接口,以达到最大程度上的复用。拿一个生产水杯(cup)的工厂举例:起初,不用工厂模式,我必须在生产水杯之前知道水杯的材料和形状等水杯的所有特征才能生产,这就是我们的new Cup();这个Cup必须是具体的。
厂主发现同一形状的被子,只是材料不同,如一个是玻璃(glass)的,一个是瓷(china)的,但是确要两条生产线,显然有资源浪费的嫌疑。现在厂主生产杯子时先不让生产线知道我要产的是玻璃的还是瓷的,而是让它在不知道具体材料的情况下先做它能做的,等到它把模具做好,只需要向其中填充玻璃原料或者瓷原料就可以造出同一形状的具体杯子了。所以就有了简单工厂模式。原来是Cup cup=new Cup;现在是SimpleCupFactory.createCup(String cupName),根据cup的名字生产Cup,而createCup返回的是一个实现了 Cup接口或抽象类的具体Cup。
简单抽象工厂模式有一个问题,就是当我现在想生产一个同样形状的铁杯时,工厂里并没有定义相应的处理流程,只能更改createCup方法,这就不合理了。我现在只是想生产铁杯,你只要在最后的时候把玻璃原料换成铁的不就行了吗,干嘛还要更改整条生产线呢?于是就有了工厂模式。原来生产线在生产模具的时候还要考虑是为玻璃杯生产的模具还是为铁杯生产的模具,现在它不用管了。CupFactory.createCup()创建Cup.CupFactory是接口或抽象类。实现它的具体子类会创建符合Cup接口的具体Cup。那么现在厂主想要生产水壶(kettle),用工厂模式就不得不再造一条水壶生产线,能不能在水杯生产线同时生产水壶呢?这就是抽象工厂模式。在原CupFactory中加一个createKettle()方法,用来生产水壶。设计之旅,未完待续......
大话设计模式之观察者模式
最后更新于:2022-04-01 06:33:12
从前,有个放羊娃,每天都去山上放羊,一天,他觉得十分无聊,就想了个捉弄大家寻开心的主意。他向着山下正在种田的农夫们大声喊:“狼来了!狼来了!救命啊!”农夫们听到喊声急忙拿着锄头和镰刀往山上跑,他们边跑喊:“不要怕,孩子,我们来帮你打恶狼!”农夫们气喘吁吁地赶到山上一看,连狼的影子也没有!放羊娃哈哈大笑:“真有意思,你们上当了!”农夫们生气地走了。第二天,放羊娃故伎重演,善良的农夫们又冲上来帮他打狼,可还是没有见到狼的影子。放羊娃笑得直不起腰:“哈哈!你们又上当了!哈哈!”大伙儿对放羊娃一而再再而三地说谎十分生气,从此再也不相信他的话了。
过了几天,狼真的来了,一下子闯进了羊群。放羊娃害怕极了,拼命地向农夫们喊:“狼来了!狼来了!快救命呀!狼真的来了!”农夫们听到他的喊声,以为他又在说谎,大家都不理睬他,没有人去帮他,结果放羊娃的许多羊都被狼咬死了。小时候总是缠着爸爸给我讲故事,爸爸不厌其烦的讲着,我乐呵呵的听着,就这样我童年的生活在爸爸多姿多彩的故事中度过,当年听这个故事,只有一个感悟,做人要诚实,然而今天我换了一个角度看这个故事,摇身一变,她就成了我的设计模式--观察者模式。
所谓的观察者模式,将复杂对象的创建与表示分离,使得同样的构建过程可以创建不同的表示。定义有点抽象,说白了就是:同一个对象,同样的操作流程,不同的操作细节,可以表现出不同的外观。首先来看一下我们观察者模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743a75050.jpg)
依然以上述狼来了的故事为例,看看这个故事究竟是怎样在我们的编程中实现呢?简单概括上述的故事就是,狼来了,放羊娃通知,农夫放下手头工作赶来救羊:
~~~
using System;
using System.Collections.Generic;
using System.Text;
namespace 观察者模式
{
class Program
{
static void Main(string[] args)
{
//狼来了
Wolf wolf = new Wolf();
//锄草的农夫
WeedingObserver Farmer1 = new WeedingObserver();
//浇水的农夫
WateringObserver Farmer2 = new WateringObserver();
wolf.Update += new EventHandler(farmer1.StopWeeding);
wolf.Update += new EventHandler(farmer2.StopWatering);
//狼来了
wolf.SubjectState = "狼来了!";
//发出通知
wolf.Notify();
Console.Read();
}
}
//通知者接口
interface Subject
{
void Notify();
string SubjectState
{
get;
set;
}
}
//事件处理程序的委托
delegate void EventHandler();
class Shepherd: Subject
{
//声明一事件Update,类型为委托EventHandler
public event EventHandler Update;
private string action;
public void Notify()
{
Update();
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
class Wolf: Subject
{
//声明一事件Update,类型为委托EventHandler
public event EventHandler Update;
private string action;
public void Notify()
{
Update();
}
public string SubjectState
{
get { return action; }
set { action = value; }
}
}
//锄草的农夫
class WeedingObserver
{
private string name;
private Subject sub;
public WeedingObserver(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
//停止锄草工作
public void StopWeeding()
{
Console.WriteLine("{0} {1} 停止锄草工作,赶去救羊!", sub.SubjectState, name);
}
}
//浇水的农夫
class WateringObserver
{
private string name;
private Subject sub;
public WateringObserver(string name, Subject sub)
{
this.name = name;
this.sub = sub;
}
//停止浇水
public void StopWatering()
{
Console.WriteLine("{0} {1} 停止浇水工作,赶去救羊!", sub.SubjectState, name);
}
}
}
~~~
观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主体对象,这个主题对象在状态发生变化时,会通知所有观察者。当一个对象改变需要同时改变其他对象,而且他不知道具体有多少对象需要改变的时候,考虑使用观察者模式。设计之旅,未完待续......
大话设计模式之建造者模式
最后更新于:2022-04-01 06:33:10
盘古开辟了天地,用身躯造出日月星辰、山川草木。那残留在天地间的浊气慢慢化作虫鱼鸟兽,为这寂静的世界增添了生气。这时,有一位女神女娲,在这莽莽的原野上行走。她放眼四望,山岭起伏,江河奔流,丛林茂密,草木争辉,天上百鸟飞鸣,地上群兽奔驰,水中鱼儿嬉戏,草中虫之豸跳跃,这世界按说也点缀得相当美丽了。但是她总觉得有一种说不出的寂寞,越看越烦,孤寂感越来越强烈,连自己也弄不清楚这是为什么。与山川草木诉说心中的烦躁,山川草木根本不懂她的话;对虫鱼鸟兽倾吐心事,虫鱼鸟兽哪能了解她的苦恼。
她颓然坐在一个池塘旁边,茫然对池塘中自己的影子。忽然一片树叶飘落池中,静止的池水泛起了小小的涟漪,使她的影子也微微晃动起来。她突然觉得心头的结解开了,是呀!为什么她会有那种说不出的孤寂感?原来是世界是缺少一种像她一样的生物。想到这儿,她马上用手在池边挖了些泥土,和上水,照着自己的影子捏了起来。捏着捏着,捏成了一个小小的东西,模样与女娲差不多,也有五官七窍,双手两脚。捏好后往地上一放,居然活了起来。女娲一见,满心欢喜,接着又捏了许多。她把这些小东西叫作“人”。今天我们的大话设计就从女娲抟土造人的故事开始说起,女娲要建造一个人,那首先要造头,身体,胳膊和腿,这就关乎到一个制造工序的问题,不知道读者有没有这样的感觉,建造者模式和工程模式很接近,但是建造者模式提供了一个更加细粒度的对象的建造过程,我们先来看一下,建造者模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_56837439cd31e.jpg)
依然以我们上述的女娲抟土造人为例,看看我们的代码,首先,女娲要先创建一个需要建造的对象,如下:
~~~
//要构造的对象
public class Robust
{
String head;
String body;
String arm;
String leg;
public String getHead()
{
return head;
}
public void setHead(String head)
{
this.head = head;
}
public String getBody()
{
return body;
}
public void setBody(String body)
{
this.body = body;
}
public String getArm()
{
return arm;
}
public void setarm(String arm)
{
this.arm = arm;
}
public String getLeg()
{
return leg;
}
public void setLeg(String leg)
{
this.leg = leg;
}
}
~~~
然后有一个构造者的接口,其中包括了建造者会使用的建造过程
~~~
public interface Builder
{
public void buidBody(String body);
public void buidHead(String head);
public void buidArm(String arm);
public void buidLeg(String leg);
public Robust getRobust();
}
public class RobustBuilder implements Builder
{
Robust robust = new Robust();
public void buidBody(String body)
{
robust.setBody(body);
}
public void buidHead(String head)
{
robust.setHead(head);
}
public void buidArm(String arm)
{
robust.setArm(arm);
}
public void buidLeg(String leg)
{
robust.setLeg(leg);
}
public Robust getRobust()
{
return robust;
}
}
~~~
有了建造者之后,需要有一个指挥家来指示建造的过程:
~~~
//指挥建造的类
public class Director
{
public Robust Construct(Builder builder)
{
builder.buidBody(null);
builder.buidHead(null);
builder.buidArm(null);
builder.buidLeg(null);
return builder.getRobust();
}
}
~~~
这样,女王抟土造人的过程就完成了,从代码角度来说, 如果我们希望分离复杂类型构建规则和类型内部组成,或者希望把相同的构建过程用于构建不同类型的时候可以考虑使用建造者模式。从应用角度来说, 如果我们希望解耦产品的创建过程和产品的具体配件,或者我们希望为所有产品的创建复用一套稳定并且复杂的逻辑的时候可以考虑使用建造者模式。设计之旅,未完待续......
大话设计模式之外观模式
最后更新于:2022-04-01 06:33:07
年年作品展,岁岁不同样,鹅黄新绿涟漪泛起思想的火花却不尽相同,十期的作品展,从13年3月20号开始到完美落幕,时至今日,她已经在我的记忆中成为过去,这朵小小的浪花激起的涟漪渐渐褪去,也许已没有也许,但那抹如琉璃般的记忆也在这片平静中渐渐凝成永恒。当再次凝眸十一期的那群孩子,笑意浅浅,心事微澜。今天是十一期作品展的日子,看到她们多像去年的我,揣测激情与梦想来到这淡紫色蒲公英飞舞的土地。昨天晚上抽空去看了看十一期的作品展彩排,彩排的过程中,有五彩的灯光,有展示作品的大屏幕,投影仪,还有音响,在整个作品展示的过程中,十一期孩子的后勤人员中,有人负责关闭灯光和打开灯光,有人负责打开和关闭投影仪,一个个作品的衔接需要打开和关闭大屏幕,有的作品是音乐类的软件,这个时候就要控制音量,打开和关闭音响,咦?这一连串的流程怎么会如此熟悉,猛然间,恍惚,这个俨然就是我们大话设计中的外观模式啊。
外观模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。首先我们来看一下我们外观模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743923649.jpg)
依然以我们的上述十一期作品展为例,来看看我们的类图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743933095.jpg)
画好了类图,来看看我们代码是如何实现的呢?我们先来看看灯光,投影仪,大屏幕,音响的代码:
~~~
using System;
namespace Facade
{
///
// 投影仪
public class Projector
{
public void OpenProjector()
{
Console.WriteLine("打开投影仪");
}
public void CloseProjector()
{
Console.WriteLine("关闭投影仪");
}
}
//灯光
public class Light
{
public void OpenLight()
{
Console.WriteLine("打开灯光");
}
public void CloseLight()
{
Console.WriteLine("关闭灯光");
}
}
//屏幕
public class Screen
{
public void OpenScreen()
{
Console.WriteLine("打开屏幕");
}
public void CloseScreen()
{
Console.WriteLine("关闭屏幕");
}
}
//音响
public class Acoustics
{
public void Acoustics ()
{
Console.WriteLine("打开音响");
}
public void CloseAcoustics ()
{
Console.WriteLine("关闭音响");
}
}
}
~~~
外观类中的代码:
~~~
public class ProductionFacade
{
/// 在外观类中必须保存有子系统中各个对象
private Projector projector;
private Light light;
private Screen screen;
private Acoustics acoustics;
public ProductionFacade()
{
projector = new Projector();
light = new Light();
screen = new Screen();
acoustics=new Acoustics();
}
///
/// 作品开始
///
public void OpenProduction()
{
//先打开灯光
light.OpenLight();
//打开投影
projector.OpenProjector();
//再打开屏幕
screen.OpenScreen();
//再打开音响
acoustics.OpenAcoustics();
}
///
/// 作品结束
///
public void CloseProduction()
{
//关闭灯光
light.CloseLight();
//关闭投影
projector.CloseProjector();
//关闭屏幕
screen.CloseScreen();
//关闭音响
acoustics.CloseAcoustics();
}
}
}
~~~
客户端的代码:
~~~
static void Main(string[] args)
{
Facade.ProductionFacade movie = new Facade.ProductionFacade();
Facade.Projector projector = new Facade.Projector();
//首先是观看作品
movie.OpenProduction();
Console.WriteLine();
//最后就是关闭作品了
movie.CloseProduction();
Console.ReadKey();
}
~~~
简单的说,就是外观模式将一个或者多个类的复杂的操作进行了隐藏,只显示一个一致的界面供客户端使用。需要注意的是,外观模式仅仅是给我们提供了更为直接和容易地操作方式,她并没有把原来的子系统进行隔离,所以,如果你还需要子系统类的更高层的功能,还是可以使用原来的子系统,这个是外观模式的一大优点,通过外观模式可以将子系统的接口上建立一个高层接口,并且将这个高层接口提供给客户端使用,这样便开业解除掉客户端和复杂子系统之间的耦合。
十一期的作品展今天晚上在学府拉开序幕,祝福你们,愿这朵小小的浪花在IT的海洋里激起惊涛骇浪,我的大话设计依然在继续........
大话设计模式之模板方法模式
最后更新于:2022-04-01 06:33:05
模板?生科的小朋友或许会这样理解,DNA复制或转录时,用来产生互补链的核苷酸序列。在我们的日程生活中,我们也时常与模板打交道,比如有些师哥师姐毕业了,需要写简历应聘,这个时候就有了简历模板;临近毕业大四的孩子,需要准备论文,就有了论文模板,所以,模板的概念就是有一个规定的格式,然后每个人都可以根据自己的需求或情况去更新完善它,例如简历模板,从网上down下来的简历模板的格式都是相同的,要想拿到一份满意的offer,我们首先要根据自己的情况填充不同的内容完成属于自己的简历,拿到那梦寐以求的Offer。
在设计模式中,模板方法模式中模板和生活中模板概念非常类似。模板方法模式——在一个抽象类中定义一个操作中的算法骨架(对应于生活中的大家down的模板),而将一些步骤延迟到子类中去实现(对应于我们根据自己的情况向模板填充内容)。模板方法使得子类可以不改变一个算法的结构前提下,重新定义算法的某些特定步骤,模板方法模式把不变行为搬到超类中,从而去除了子类中的重复代码。来看一下我们模板方法模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_568374380f3a1.jpg)
除了简历模板之外,我们再来举一个生活中的故事,而我就是这个故事中傻傻的主角儿,这几天饭卡里没有钱了,人是铁饭是钢,一顿不吃饿得慌,虽然说减肥刻不容缓,可是真理说的好啊,不吃饱哪儿来的力气减肥啊,周二下午四点来到工商银行ATM机钱取钱,奇怪,为什么密码不对呢?无奈,再来一次,就这样连续三次,我的银行卡光荣的被锁住了,拔出银行卡一看,我看见了什么,上面赫然写着,邮政储蓄,原来我把邮政储蓄的卡当成了工行的卡,两个卡的密码不一样,我在工行取钱,插到ATM机的是邮政的卡,按的密码是邮政银行卡的密码,所以,才发生了刚才那不堪回首的一幕,没有办法,去前台把卡给解锁了,这样才能有力气减肥。
来到服务厅,人真多啊,在银行办理业务时,一般我们都按照这样的步骤依次进行,取号排队,办理具体业务,对银行工作人员进行评分,这样一套工作流程不就是我们的模板方法模式?我们先来看看这个例子的类图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743820b2d.jpg)
有了类图,我们的代码需要如何实现呢:
~~~
// 客户端调用
class Client
{
static void Main(string[] args)
{
// 创建一个办业务流程实例并调用模板方法
Business business = new Business();
business. Flow();
Console.Read();
}
}
public abstract class Business
{
// 模板方法,不要把模版方法定义为Virtual或abstract方法,避免被子类重写,防止更改流程的执行顺序
public void Flow()
{
Console.WriteLine("办理业务的一般流程");
this.LineUp(); //取号排队
this.Manage(); //办理具体业务
this.Graded(); //对银行工作人员进行评分
}
// 第一步取号排队
public void LineUp()
{
Console.WriteLine("取号排队");
}
// 第二步办理具体业务
public void Manage()
{
Console.WriteLine("办理具体业务");
}
// 第三步对银行工作人员进行评分
public void Graded()
{
Console.WriteLine("对银行工作人员进行评分");
}
}
// 顾客A
public class Business: Flow
{
public override void Business()
{
Console.WriteLine("顾客A办理业务");
}
}
// 顾客B
public class Business : Flow
{
public override void Business()
{
Console.WriteLine("顾客B办理业务");
}
}
~~~
经过银行姐姐的辛苦工作,我的卡终于解锁了,以后做事情要小心一点,不能这么马虎了,都这么大个人了,是吧,言归正传,模板方法模式--准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模版方法模式的用意。设计之旅,未完待续.......
大话设计模式之原型模式
最后更新于:2022-04-01 06:33:03
外国人把那京戏叫做“Beijing Opera ” 没见过那五色的油彩楞往脸上画,四击头一亮相,(哇……)美极了妙极了,简直“ok”顶呱呱 ,蓝脸的多尔敦盗御马,红脸的关公战长沙 ,黄脸的典韦白脸的曹操 ,黑脸的张飞叫喳喳…… ,细心的小朋友,仔细区分就会发现,虽然每个京剧演员都不同,但基本上只具有几种脸型,长方形,圆形,细长,然后配上不同的妆容,胡子,眉毛,头饰,服装,有的再加点儿装饰物,就成了我们所看到的不同的演员角色,国粹和我们的编程有着什么样的联系呢?用面向对象的方法来说就是,我们先建立一个原型,然后通过对原型进行复制和修饰的方法,就可以产生一个和原型相似的新对象,用GOF的话来说就是,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
在大话设计模式,简历复印-原型模式这一节中,当我们创建一个类的实例的过程很复杂,并且我们需要创建多个这样类的实例时,如果我们用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在内存中分配了多个一样的类实例对象,然后如果采用工厂模式来创建这样的系统的话,随着产品类的不断增加,导致子类的数量不断增多,反而增加了系统复杂程度,所以在这里使用工厂模式来封装类创建过程并不合适,然而原型模式可以很好地解决这个问题,因为每个类实例都是相同的,当我们需要多个相同的类实例时,没必要每次都使用new运算符去创建相同的类实例对象,此时我们一般思路就是想——只创建一个类实例对象,如果后面需要更多这样的实例,可以通过对原来对象拷贝一份来完成创建,这样在内存中不需要创建多个相同的类实例,从而减少内存的消耗和达到类实例的复用。 然而这个思路正是原型模式的实现方式。下面我们来看看原型模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743748647.jpg)
前天,晚上放学回宿舍,一个不小心把钥匙弄丢了,结果进不了宿舍门,要是两个不小心还得了,呜呜,谢谢颖杰帮我开门,没办法啊,总要人家开门多不好,还是自己配一把吧,宿舍钥匙和车钥匙在一个钥匙圈上,幸好车钥匙还有一把备份,找个时间去配一把宿舍钥匙和车钥匙,次日中午我拿着颖杰的钥匙,来到中门,配钥匙的师傅的手艺可真是熟练啊,只见我把钥匙给了他,他直接找一个合适的钥匙胚子,把我的钥匙夹在配钥匙机的一端,胚子夹在另一端,一开电源,一把标尺比着我的钥匙齿型走一遍,砂轮就在胚子上复制出一把钥匙来!一分钟不到,两把新钥匙就搞定了!这里的旧钥匙是一个原型。配钥匙的过程就是根据我提供的原型,再复制一份出来,就有了一个新的钥匙。两个钥匙完全一样。我可以给新配的钥匙加上一个标签,以表明是我的,不能和颖杰的弄混了,再来看看我们的代码是如何实现的呢:
~~~
namespace Prototype
{
//抽象钥匙原型
public abstract class Key
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private string owner;
public string Owner
{
get { return owner; }
set { owner = value; }
}
public Key(string name, string owner)
{
this.name = name;
this.owner = owner;
}
//钥匙复制自身的抽象定义
public abstract Key Clone();
public override String ToString()
{
return this.Name + ", belongs to " + this.Owner;
}
}
//宿舍钥匙
public class DormitoryKey : Key
{
public DormitoryKey(string owner) : base(" Dormitory Key", owner) { }
public override Key Clone()
{
return new DormitoryKey(this.Owner);
}
}
//自行车钥匙
public class BicycleKey : Key
{
public BicycletKey(string owner) : base("Bicycle Key", owner) { }
public override Key Clone()
{
return new BicycleKey(this.Owner);
}
}
//客户端调用方法
public class Client
{
public static void Main(string[] args)
{
Key oldDormitoryKey, newDormitoryKey, oldBicycleKey, newBicycleKey;
oldDormitoryKey = new DormitoryKey("yingjie");
newDormitoryKey = oldDormitoryKey.Clone();
newDormitoryKey.Owner = "Me";
oldBicycleKey = new BicycleKey("Me");
newBicycleKey = oldBicycleKey.Clone();
newBicycleKey.Owner = "yingjie";
Console.WriteLine(oldDormitoryKey);
Console.WriteLine(newDormitoryKey);
Console.WriteLine(oldBicycleKey);
Console.WriteLine(newBicycleKey);
}
}
}
~~~
原型模式在生成复杂对象比较苦难的环境中比较适用,通过克隆已有对象来实现创建新的对象,节省了时间和空间,原型模式应用于希望系统独立于产品的创建、表示和构成时,这和工厂模式很类似。事实上,和工厂模式相同的是,原型模式同样对客户隐藏了对象的创建工作,但是,与工厂模式通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。工厂模式适用于产品种类有限的情况下,当产品数量巨大或需要提供动态产品增删等性能时,使用原型模式具有更强的适应性。设计之旅,未完待续......
大话设计模式之工厂模式
最后更新于:2022-04-01 06:33:00
千百年来,关于“空中花园”有一个美丽动人的传说。新巴比伦国王尼布甲尼撒二世娶了米底的公主安美依迪丝为王后。公主美丽可人,深得国王的宠爱。可是时间一长,公主愁容渐生。尼布甲尼撒不知何故。公主说:“我的家乡山峦叠翠,花草丛生。而这里是一望无际的巴比伦平原,连个小山丘都找不到,我多么渴望能再见到我们家乡的山岭和盘山小道啊!”原来公主得了思乡病。于是,尼布甲尼撒二世令工匠按照米底山区的景色,在他的宫殿里,建造了层层叠叠的阶梯型花园,上面栽满了奇花异草,并在园中开辟了幽静的山间小道,小道旁是潺潺流水。工匠们还在花园中央修建了一座城楼,矗立在空中,巧夺天工的园林景色终于博得公主的欢心。故事讲到这里,在我们的故事中蕴藏着怎样的设计模式呢?今天这篇博文的内容就从我们美丽动人的传说空中花园开始!
如果空中花园中只种花,那么就用[简单工厂](http://blog.csdn.net/u010850027/article/details/21878639)就可以了,如果公主喜欢各种各样的花,种类比较繁多,我们就需要用工厂方法,把公共有的东西抽象出来,再者如果公主想要扩大花园的规模,一个在安徽一个在河北,这样工厂方法也就无法实现了,这个时候,就需要用到抽象工厂,把各种各样的植物,又组成一个空中花园。我们先来看一下工厂方法模式模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_5683743676b82.jpg)
在前面的博文中,我们学习过[简单工厂模式](http://blog.csdn.net/u010850027/article/details/21878639),简单工厂是一个工厂只生产一类的产品,面对的是具体的类,工厂方法是可以生产不同的产品,把公共的方法抽象出来,然后进行创建各种各样的产品.抽象工厂把几种产品划出共同的东西,把相互依赖的对象抽象出来,只要实现这些接口就可以得到不同的产品,简单工厂模式系统难以扩展,一旦添加新产品就不得不修改简单工厂方法,这样就会造成简单工厂的实现逻辑过于复杂,下面就具体看看工厂模式是如何解决该问题的。 工厂方法模式之所以可以解决简单工厂的模式,是因为它的实现把具体产品的创建推迟到子类中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式就可以允许系统不修改工厂类逻辑的情况下来添加新产品,这样也就克服了简单工厂模式中缺点。下面看下工厂模式的具体实现代码(这里还是以简单工厂模式中点菜的例子来实现:
~~~
namespace 设计模式之工厂方法模式
{
///
/// 菜抽象类
///
public abstract class Food
{
// 输出顾客点了什么菜
public abstract void Print();
}
/// 酸辣土豆丝这道菜
public class ChiliSourPotatod: Food
{
public override void Print()
{
Console.WriteLine("酸辣土豆丝出锅!");
}
}
///
/// 姜汁皮蛋这道菜
///
public class PreservedEggSinGingerSauce : Food
{
public override void Print()
{
Console.WriteLine("姜汁皮蛋");
}
}
///
/// 抽象工厂类
///
public abstract class Creator
{
// 工厂方法
public abstract Food CreateFoddFactory();
}
///
/// 酸辣土豆丝工厂类
///
public class ChiliSourPotatod:Creator
{
///
/// 负责创建酸辣土豆丝这道菜
///
///
public override Food CreateFoddFactory()
{
return new ChiliSourPotatod();
}
}
///
/// 姜汁皮蛋工厂类
///
public class PreservedEggSinGingerSauceFactory:Creator
{
///
/// 负责创建姜汁皮蛋这道菜
///
///
public override Food CreateFoddFactory()
{
return newPreservedEggSinGingerSauce();
}
}
///
/// 客户端调用
///
class Client
{
static void Main(string[] args)
{
// 初始化做菜的两个工厂()
Creator ChiliSourPotatodFactory = new ChiliSourPotatodFactory();
Creator PreservedEggSinGingerSauceFactory = new PreservedEggSinGingerSauceFactory();
// 开始做酸辣土豆丝
Food ChiliSourPotatod = tomatoScrambledEggsFactory.CreateFoddFactory();
ChiliSourPotatod.Print();
//开始做姜汁皮蛋
FoodPreservedEggSinGingerSauce =PreservedEggSinGingerSauceFactory.CreateFoddFactory();
PreservedEggSinGingerSauce.Print();
Console.Read();
}
}
}
~~~
使用工厂方法实现的系统,如果系统需要添加新产品时,我们可以利用多态性来完成系统的扩展,对于抽象工厂类和具体工厂中的代码都不需要做任何改动。例如,我们我们还想点一个“黄瓜炒鸡蛋”,此时我们只需要定义一个黄瓜炒鸡蛋具体工厂类和黄瓜炒鸡蛋类就可以。而不用像简单工厂模式中那样去修改工厂类中的实现,具体代码如下所示:
~~~
///
/// 黄瓜炒鸡蛋这道菜
///
public class ScrambleEggWithCucumber : Food
{
///
/// 重写抽象类中的方法
///
public override void Print()
{
Console.WriteLine("黄瓜炒鸡蛋好了");
}
}
///
/// 黄瓜炒鸡蛋工厂类,负责创建黄瓜炒鸡蛋这道菜
///
public classScrambleEggWithCucumberFactory : Creator
{
///
/// 负责创建黄瓜炒鸡蛋这道菜
///
///
public override Food CreateFoddFactory()
{
return new ScrambleEggWithCucumber();
}
}
///
/// 客户端调用
///
class Client
{
static void Main(string[] args)
{
// 如果客户又想点黄瓜炒鸡蛋了
// 再另外初始化一个黄瓜炒鸡蛋工厂
Creator ScrambleEggWithCucumberFactor = new ScrambleEggWithCucumberFactory();
// 利用黄瓜炒鸡蛋工厂来创建黄瓜炒鸡蛋这道菜
Food ScrambleEggWithCucumber = ScrambleEggWithCucumberFactor.CreateFoddFactory();
ScrambleEggWithCucumber.Print();
Console.Read();
}
}
~~~
简单工厂:严格说并不是一个设计模式。简单工厂没有抽象类,只有一个具体工厂类如MyFactory,然后MyFactory里面有个工厂方法CreateProduct返回一个基类产品,具体返回什么具体实例通过传入参数然后用case判断。用手机生产做个例子:比如魅族简单工厂就是只有MeizuFactory工厂类,工厂方法就是CreateMeizuPhone,参数是Meizu手机型号,根据不同型号创建不同的Meizu手机(使用case)。很明显的缺点就是Meizu每发明一个新型号的手机都需要修改简单工厂类(增加case判断),违反了封闭修改,开放扩展原则。
工厂方法:该模式有一个抽象基类和若干个派生的具体工厂类,基类定义了一个虚工厂方法返回指定产品类的基类,派生类需要实现该虚方法并创建具体产品类返回。注意工厂方法的每个具体工厂只负责返回一种产品类。同样以手机生产做例子:Meizu工厂方法模式有一个工厂基类MeizuFactory,注意此工厂和上面不一样,是抽象的。该类定义一个虚工厂方法CreateMeizuPhone,该方法返回MeizuPhone基类。然后不同型号的手机对应一个该型号的手机工厂,这样的优点就是,新出一个Meizu手机型号,只需派生一个该型号的工厂而无需修改原来的代码。符合封闭修改,开放扩展原则。设计之旅,未完待续......
大话设计模式之代理模式
最后更新于:2022-04-01 06:32:58
白龙马脖铃儿急,颠簸唐玄奘小跑仨兄弟,西天取经不容易容易干不成大业绩。还记得吴承恩的西游记伴随着我走过了多少快乐的童年时光,当时年幼,殊不知,西游记中也蕴藏着设计模式,且听我娓娓道来:
却说那师徒二人,悟空牵着小白龙,与唐僧继续赶路西行,虽说取经普度众生刻不容缓,可是太阳公公要休息不是,于是太阳公公悄悄落入地平线,把余晖金灿灿的洒向大地。悟空见天色渐晚,腾空一看,不远处有一村庄,问一人得知此村为高老庄,敲一门得知,这家主人为猪八戒的丈人高太公家。原来猪八戒要逼迫高太公把女儿翠兰嫁给自己,为了将高家小姐解救出八戒的魔掌,悟空决定扮做高小姐,会一会这个妖怪:"行者却弄神通,摇身一变,变得就如那女子一般,婀娜多姿,沉鱼落雁,独自个坐在房里等那妖精。不多时,一阵风来,真个是走石飞砂……那阵狂风过处,只见半空里来了一个妖精,果然生得丑陋:黑脸短毛,长喙大耳,穿一领青不青、蓝不蓝的梭布直裰,系一条花布手巾……走进房,一把搂住,就要亲嘴……"上面的小故事,是我们大话设计中哪种模式呢?聪明的读者肯定知道,就是我们设计模式中的代理模式,孙悟空变成高小姐的样子代替高小姐婚配猪八戒,首先,我们来看一下代理模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_568374355d5e5.jpg)
再回来我们的故事中,看看故事中的结构图中是什么样子的呢?
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_568374356e710.jpg)
看看我们的这个故事用我们的代码又是如何实现的呢?首先,我们来看看我的代理接口“娶媳妇”:
~~~
interface IGetMarried
{
void Hug();
void GetMarried();
}
~~~
猪八戒类如下:
~~~
class Zhubajie:IGetMarried
{
gaolaozhuang miss gao;
public Zhubajie (gaolaozhuang miss gao)
{
this.Miss gao=Miss gao;
}
public void Hug()
{
console.WriteLine("拥抱")
}
public void GetMarried()
{
console.WriteLine("成亲")
}
}
~~~
代理孙悟空类的代码如下:
~~~
class proxy:IGetMarried
{
zhubajie bajie;
public proxy(Miss gao)
{
bajie=new bajie(Miss gao);
}
public void Hug()
{
bajie.Hug();
}
public void GetMarried()
{
bajie.GetMarried();
}
}
~~~
客户端代码如下:
~~~
static void Main(string[] args)
{
gaolaozhuang miss gao =new Miss gao();
miss gao.namespace="高小姐";
proxy daili =new proxy(miss gao);
daili.Hug();
daili.GetMarried();
Console.Read();
}
~~~
代理模式英文名Proxy Pattern,是23种常用设计模式之一,她的功能是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
代理貌似跟我们前面学习过的[委托](http://blog.csdn.net/u010850027/article/details/21376275)很容易混淆,浅谈一下我对他们的认识,某个不想被外界访问或者禁止直接被外界的对象,此时需要有一个中介使其能够与外界进行通讯,而种中介就称做代理。而C#中的委托(Delegate)有些不同,其本质是一种函数指针,其只是把对象委托给它的方法原封不动地提供给其他对象调用,并不能做任何的修饰。而代理并不是按照原接口提供出去,其做法是自身定义一个与被代理对象相同或者相似的接口提供给外界调用,因为提供的方法完全由代理本身的性质决定。设计之旅,未完待续......
大话设计模式之装饰模式
最后更新于:2022-04-01 06:32:56
装饰,字面意思是对生活用品或生活环境进行艺术加工的手法。它必须与所装饰的客体有机地结合,成为统一、和谐的整体,以便丰富艺术形象,扩大艺术表现力,加强审美效果,并提高其功能、经济价值和社会效益。我们编程世界中的装饰又有着怎样与众不同的解释呢?原来装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
我们来看一个具体的例子,经过一个上午的消耗,加上昨天晚上熬夜看《出彩中国人》,早上赖床,没有去吃饭,那上午叫一个饿啊,于是发誓,再也不熬夜了,咳咳,到了晚上,拿起手机什么又都忘了,于是,想着赶紧下课,去吃饭,最好来一碗面条,来点儿辣椒再来点儿醋,那味道,呼呼流口水了,来到卖面条的地方,这个点儿来的人可真多啊,咱是好孩子,得排队是不是,顺便看一下标签上有哪些面条,哇塞面条的种类可真多啊,比如有雪菜肉丝面条,西红柿鸡蛋面条,小鸡蘑菇面条,听,我前面的小姑娘要了一碗西红柿鸡蛋面条,紧接着一个男孩要了一碗小鸡蘑菇面条,每个同学的选择是不同的,也就是需求是各种各样的,那么这种情况在我们的编程世界中如何实现呢,这个时候,排队的我想到了继承,如下图所示:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_56837434b1c56.jpg)
这个时候如果我想要加醋和加辣椒的面条?我要怎么办,可以通过继承来实现扩展,但是这样的设计有点儿笨笨的,于是一种新设计模式--装饰模式就这样横空出世了,我们来看看装饰模式的结构图:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-12-30_56837434c0f8e.jpg)
装饰者模式呢,其实可以看做是一种在已有功能上动态添加新的功能的一种方式,在不用装饰者模式的前提下,如果要在已有的功能上添加新功能,一般都是可以使用继承的,但是,继承的缺点呢,在上面的例子中也暴露的很明显,同时,使用继承的话,添加功能不是动态的,因为子类完全继承了父类,而使用装饰者模式的话,您可以在客户端按照需求一个一个的包装对象,通过包装对象来添加新功能,这样便实现了动态添加新功能,比如,我可以对 Component 通过 ConcreteDecoratorA 来包装一个 State 状态,或者是通过 ConcreteDecoratorB 来包装一个新的行为(功能)Behavior ,以我们的面条例子为例,看看我们的程序是怎么实现的呢?
先来看一下Eat类:
~~~
using System;
namespace Decorator
{
public abstract class Noodle
{
///
/// 在抽象类中只定义了一个抽象接口
/// 然后可以通过这个抽象接口来给对象动态的添加功能
///
public abstract void ShowNoodle();
}
}
~~~
然后就是一个Noodle类:
~~~
using System;
namespace Decorator
{
public class Noodle : Eat
{
private string name;
public Noodle(string name)
{
this.name = name;
}
///
/// 给当前的对象添加一些功能
/// 比如这里就是指定了面条的名称
/// 这里添加的功能是静态添加的
///
public override void ShowEat()
{
Console.WriteLine("面条名称为:{0} ", this.name);
}
}
}
~~~
下面再来看 DecoratorEat 类:
~~~
namespace Decorator
{
public class DecoratorEat: Noodle
{
///
/// 在装饰类中必须要保存一个对于对象的引用
///
protected Eat eat;
public DecoratorEat(Eat eat)
{
this.eat = eat;
}
public override void ShowEat()
{
if (Eat != null)
{
Eat.ShowEat();
}
}
}
}
~~~
还有就是装饰类 Tomato:
~~~
using System;
namespace Decorator
{
public class Tomato : DecoratorEat
{
///
///
public Tomato(Eat eat)
: base(eat)
{
}
public override void Showeat()
{
//首先必须要调用父类的 ShowEat
base.ShowEat();
//然后下面就可以添加新功能了
Console.WriteLine("加西红柿 ");
}
}
}
~~~
装饰类鸡蛋和豆皮的代码跟上述装饰类西红柿雷同,再此不一一赘述,接下来,我们一起看看客户端的代码:
~~~
using System;
using Decorator;
namespace DecoratorTest
{
class Program
{
static void Main(string[] args)
{
Noodle noodle = new Noodle("面条");
//给面条加西红柿,也就是使用西红柿来装饰面条
Tomato tomato = new Tomato(noodle);
//给加了西红柿的面条加鸡蛋,也就是使用加鸡蛋来装饰面条
Egg egg = new Egg(Tomato);
//显示出当前面条的状态
Egg.ShowNoodle();
Console.WriteLine();
noodle = new Noodle("面条");
tomato = new Tomato(noodle);
//给加了西红柿的面条加豆皮
Doupi doupi = new Doupi(Tomato);
//给加了西红柿和鸡蛋的面条加个豆皮
Doupi doupi = new Doupi(doupi);
doupi.ShowEat();
Console.ReadLine();
}
}
}
~~~
通过装饰模式我们的小菜有着百搭的风格,而我也.......嘻嘻,再回到我们的装饰模式中来,装饰模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。设计之旅,未完待续......
大话设计模式之设计原则
最后更新于:2022-04-01 06:32:54
原则指的是说话、行事所依据的准则。 “理论与实践的统一,是马克思主义的一个最基本的原则。原则处处不在,那我们编程的世界中又有着怎样的原则呢?在我们的大话设计模式中,介绍了六种原则,下面我们对这些原则进行一一讲解。
**一、单一职责原则**
一枚小小的环形戒指,一如永世不变的约定。戒指的爱情含义,令世间所有女性为之向往。香港戴瑞珠宝集团旗下品牌Darry Ring更将戒指的含义浪漫诠释,以一生仅有一枚的奇特规定,让每一枚戒指都有只有一个专属的承诺,唯一的一枚,是为此生挚爱保留。而这一切只源于Darry Ring对真爱的追求传播,见证更多真爱故事。香港戴瑞珠宝集团旗下极富盛名的求婚戒指品牌Darry Ring,以全球首创独树一帜绑定身份证定制钻戒形式,提出了“一生?唯一?真爱”的品牌理念。在此有最浪漫的购钻规定,每位男士凭借身份证仅可定制唯一一枚Darry Ring,赠予此生唯一挚爱。
Darry Ring品牌的创立初衷,旨在见证真爱,传播真爱理念。在浮华社会里,依然存留一方对纯净美好爱情的期待,而钻戒象征的坚贞与永恒便成为了最完美的信物。一生仅有一枚Darry Ring,因一生只爱一个人。真爱,是回归初心寻找到的一份难能可贵的美好。Darry Ring的可贵,在于传播了真爱唯一的理念,让拥有一枚Darry Ring的女人享有一份专属的幸福;并以此生唯一守护的安全感,庇护着每对恋人至真至美的爱情。一份真爱协议,承载的意义绝对不亚于一份结婚证书。签署下的是一生唯一的约定,名字将永远印记在协议上,不能修改、删除,需要一生信守承诺,呵护至极。
一如我们设计模式中的单一职责原则, 就一个类而言,应该仅有一个引起它变化的原因。如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭受到意想不到破坏。软件真正要做的许多内容,就是发现职责并把那些职责相互分离。如果你能够想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责,就应该考虑类的职责分离。和我们的学习思想不谋而合,专注,高效,所以成功......
**二、开放-封闭原则**
历史走过了整整数百年。香港和澳门的回归有着划时代的意义,充分显示了按照“一个国家、两种制度”实现中国统一大业指导思想具有强大的生命力。所以,“一国两制”方针将能推进祖国的和平统一事业,实现祖国的完全统一和民族的全面振兴。
恰似我们的软件,学过软工的小朋友都知道,软件需求总是变化的,世界上没有一个软件的是不变的,因此对软件设计人员来说,必须在不需要对原有系统进行修改的情况下,实现灵活的系统扩展。所谓开放封闭原则就是软件实体应该对扩展开发,而对修改封闭。开放封闭原则是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化,降低耦合,而开放封闭原则正是对这一目标的最直接体现。开放封闭原则主要体现在两个方面:对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。对修改封闭,意味着类一旦设计完成,就可以独立其工作,而不要对类尽任何修改。
学习是为了更好的生活,愿看到这篇文章的小朋友对痛苦关闭,对快乐开放......
**三、里氏代换原则**
1988年,由麻省理工学院一位姓里的女士(Liskov)提出来,举个简单的例子,“猫”是抽象的猫,而黑猫和白猫是猫的具体子类,一只黑猫则是“黑猫”类的实例,一只白猫则是“白猫”类的实例,如果一个方法适用于猫,那么必然适用于白猫或者是黑猫。如果有一个方法,捉老鼠,这就是历史代换原则,如果对每个类型T1的对象01,都有类型为T2的对象02,使得T1定义的所有程序P在所有对象01都代换成02时,程序P的行为没有变化,那么T2为T1的子类型。
在项目中,采用里氏替换原则时,尽量避免子类的“个性”,一旦子类有“个性”,这个子类和父类之间的关系就很难调和了,把子类当做父类使用,子类的“个性”被抹杀——委屈了点;把子类单独作为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离——缺乏类替换的标准。
**四、依赖倒转原则**
所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
举个简单的例子,我们现在使用的电脑有各式的品牌,联想、神舟、戴尔等等,电脑需要用到鼠标,键盘;假设鼠标、键盘是针对某一个品牌的机器实现去做的话,那么我们将会遇到什么问题呢?那么我们市面上的键盘和鼠标就都是各式各样的,有一天鼠标,或键盘坏了,我们要怎么去买呢?难道记住这个电脑是什么品牌,什么型号,还有什么类型的去买么?这样会疯掉的。现在我们的电脑基本上都是使用USB接口的了,无论是键盘也好,鼠标也好,我们只要买USB接口的就可以使用了,同时,使用USB接口还可以有其他的扩展,只要实现了,这个接口,实现怎么样都没关系,例如,实现了USB接口的小台灯,只要接上USB线就可以照明了;又如实现了USB接口的充电器,接到我们的电脑上就可以充电了。
高层模块不应该依赖低层模块,两者都应该依赖其抽象又如何理解呢?这个问题也可以这么问:为什么要叫倒转?呢?在面向过程的开发中,为了使用常用的代码可以复用,一般都会把这些常用的代码写成许许多多函数的程序库,这样我们做新项目的时候,就去调用这些函数就可以了。
**五、迪米特法则**
迪米特法则(Law of Demeter)又叫做最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。《老子》云:"小国寡民……邻国相望,鸡犬之声相闻,民至老死,不相往来。"将被统治的对象隔离开来,使它们没有直接的通信,可以达到分化瓦解,继而分而治之的效果。迪米特法则与老子的"小国寡民"的统治之术不谋而合。
**六、合成/聚合复用原则**
聚合表示一种弱的“拥有”关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格的部分和整体的关系,部分和整体的生命周期一样。
优先使用对象的合成/聚合将有助于保持每个类的封装,并被集中在单个任务上。这样类和类继承层次会保持较小的规模,并且不太可能增长到不可控制的庞然大物。
**后记**
在我们的设计模式中,正是因为有了这些原则的存在,以及她们在代码中的灵活应用,让我们的软件更加富有创造力和生命力,单一职责原则--一生只爱你一个,开放--封闭原则-祖国统一,有你有我,你永远是我的不可分割,里氏代换原则--无论黑猫白猫抓到老鼠的猫都是好猫;依赖倒转原则--任你戴尔或是联想,我们有着相同的接口实现外部连接,迪米特法则--无为而治,小国寡民不谋而合,合成聚合--部分和整体需要好好把握....