设计模式总结

最后更新于:2022-04-01 20:11:22


OOP基础。
  1. OOP有三大目标,重用性、灵活性和扩展性。
      1. OOP有三大特性,封装、继承和多态。抽象虽然不是OOP的编程特性,但它却是所有编程语言的最大特性。
        1. 封装,隐藏程序的属性和的实现细节,并控制属性的访问权限,对外仅提供接口。
            1. 继承,是对基类的代码进行利用并扩展基类,是一个从一般到特殊的过程。
            2. 多态,运行时,根据对象的不同,同一接口,但是调用不同的实现方式来达到多态的特性。
            3. 抽象,将需要变化之处与不需要变化之处区分开来。抽象出来的类,不仅仅可能是一个事物,也可以是一组行为,凡是关联性比较强的都可以抽象为一个基类。
                  OOP六大原则。
                  1. 开闭原则,开放扩展,关闭修改。
                  2. 依赖倒置原则,高层和底层模块都依赖于抽象,也即面向接口编程。
                  3. 接口隔离原则,接口功能要单一,多个任务提供多个接口。
                  4. 合成/聚合利用原则,能够用合成/聚合的,就不要用继承。
                  5. 迪米特法则,也称最小知道原则,尽可能少的与其他类有耦合关系,有的时候可以用前向声明。
                  6. 单一职责原则,一个类只有一个引起它变化的原因,如一个“清洁”类,别吃饭时也引起“清洁”类响应。
                  7. 里氏替换原则,子类不覆盖父类方法,做到子类对象完全可以替换父类对象。

                      •  (个人觉得里氏替换原则可以不太用考虑,实际设计中,子类想完全替换父类还是有一定的困难的。)
                       常用设计模式。
                      1. 简单工厂模式,有一个提供接口的抽象类,然后实现不同功能的子类去实现抽象类,最后一个简单工厂类来完成对不同子类的创建。创建型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                      2. 工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。定义一个抽象工厂类,每一个产品,按照抽象工厂的基本要求新建一个工厂来生产新的产品创建型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                      3.  单例模式保证一个类只有一个实例,并提供一个全局访问点创建型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程  
                      4. 装饰者模式,动态地给一个对象添加一些职责。就扩展功能而言,装饰者模式比继承更加灵活。结构型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程  
                      5. 代理模式,为其他对象提供一种代理以控制对这个对象的访问。结构型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                      6. 适配器模式,将一个类的接口,转换成客户期望的另一个接口。适配器模式让原本由于接口不兼容而不能一起工作的类可以一起工作。结构型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                      7. 组合模式,将对象组合成树形结构以表示“部分—整体”的层次结构。组合模式能够让客户以一致的方式处理个别对象以及对象组合。结构型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程 
                      8. 命令模式,将一个请求封装成一个对象,从而可以用不同的请求、队列或者日志对客户进行参数化。命令模式也支持可撤销的操作。对象行为型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                      9.  模板方法模式,定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法可以不改变改变一个算法的结构即可以重定义该算法的某些特定步骤。类行为型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                      10. 迭代器模式,提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。对象行为型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                         
                      11. 状态模式,允许对象在内部状态改变时改变它的行为,对象看起来似乎修改了它的类。对象行为型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                      12. 策略模式,定义一系列算法,分别封装起来,让它们可以互相替换。此模式让算法的变化可以独立于使用算法的客户。对象行为型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                      13. 观察者模式,定义了对象之间一对多的依赖关系,当一个对象发生改变时,它的所有依赖者都会收到通知并自动更新。对象行为型模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
                      P.S.
                      1. 模块化,主要指以功能来划分的。模块化主要遵循高内聚低耦合,也即职责单一。卡内基.梅隆大学的RobertHarper教授,“面向对象编程应该完全的从基础课程中删除掉,因为它既是反模块化,又是反并行的,这是它的非常固有的特征,所以它不适合作为一种现代的计算机科学课程。”可以模块化和并行在现代程序设计中的重要性是高于OOD的一些原则的。
                      2. MVC(Model View Controller),能够将UI和数据以及对数据处理的逻辑分开,降低耦合。在《Head First设计模式》中,将MVC列为复合模式,是模式的模式,是因为MVC会使用到多个模式。设计模式总结 - 飞鹤 - 飞鹤的成长历程
            ';

            24——行为型模式之访问者模式

            最后更新于:2022-04-01 20:11:20

            **定义:**访问者模式(Vistor Pattern),表示一个作用于对象结构中各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 **类型:**对象行为型模式。 概述:         访问者模式适用的场合比较狭窄,因为它的要求非常严。ConcreteElement要求具有一样的算法接口,并且ConcreteVisitor也要求一样的接口,这两点就使得访问者模式的使用范围比较窄。在《Head First Design Patterns》中也将访问者模式列为非常用模式之一。          举一个例子,有一个仓库(对象结构),里面储存不同的电器(元素),如电视、电脑(具体元素),仓库可以按电器新旧或大小(具体的访问者)来摆放电器产品。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467f2b12.jpg) 参与者: 1. Client:构建抽象的访问者以及结构体对象。 1. Storage,具体生成并存储不同的商品(如Television、Computer)。 1. Goods,抽象商品类。 1. Television、Computer,具体的商品类,对相应的属性完成赋值。 1. Place,抽象的访问者类,提供抽象访问方法。 1. State、Size,具体的访问者类,根据相应商品属性来实现具体的摆放方法Put()。 示例代码: ~~~ using System; using System.Collections.Generic; using System.Text; namespace Pattern24 { abstract class Visitor { public abstract void PutTelevision(Television tv); public abstract void PutComputer(Computer comp); } class SizeVisitor : Visitor { public override void PutTelevision(Television tv) { Console.WriteLine("按商品大小{0}排放", tv.Size); } public override void PutComputer(Computer comp) { Console.WriteLine("按商品大小{0}排放", comp.Size); } } class StateVisitor : Visitor { public override void PutTelevision(Television tv) { Console.WriteLine("按商品新旧值{0}排放", tv.State); } public override void PutComputer(Computer comp) { Console.WriteLine("按商品新旧值{0}排放", comp.State); } } abstract class Goods { public abstract void Operate(Visitor visitor); private int nSize; public int Size { get {return nSize;} set {nSize = value;} } private int nState; public int State { get {return nState;} set {nState = value;} } } class Television : Goods { public override void Operate(Visitor visitor) { visitor.PutTelevision(this); } } class Computer : Goods { public override void Operate(Visitor visitor) { visitor.PutComputer(this); } } class Storage { private IList list = new List(); public void Attach(Goods element) { list.Add(element); } public void Detach(Goods element) { list.Remove(element); } public void Operate(Visitor visitor) { foreach (Goods e in list) { e.Operate(visitor); } } } class Program { static void Main(string[] args) { Storage storage = new Storage(); storage.Attach(new Television()); storage.Attach(new Computer()); SizeVisitor v1 = new SizeVisitor(); StateVisitor v2 = new StateVisitor(); storage.Operate(v1); storage.Operate(v2); Console.Read(); } } } ~~~ 适用性: 1. 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。 1. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。 Visitor使得你可以将相关的操作集中起来定义在一个类中。 当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。 1. 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。 改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。 如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好**               ** 优缺点: 1. 优点,完成新的模板方法是不用修改原有代码,通过添加新代码完成。 1. 缺点,当使用访问者模式时,打破了组合模式。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》
            ';

            23——行为型模式之模板方法模型

            最后更新于:2022-04-01 20:11:18

            **定义:**模板方法模式(TemplateMethod Pattern),定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模块方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 类型:类行为型模式 概述:        模板方法模式应该算是所有设计模式中最简单,却也是应用得最多的模式了。模板方法模式其实就是多态的体现。          模板方法模式设计的要点是在父类中定义好一个操作中的算法骨架,然后具体实现延迟到子类中。模板方法其实与策略模式有一些类似的地方。不同点是,策略模式将算法抽象出来成为一个新的类,而模板方法则没有将算法抽象出来。主要体现是策略模式的算法相对复杂一点,而模板方法的算法相对简单一点。         举例,有一群人,可以按身高排序然后取其中高的一部分;也可以体量排序然后取其中体量适宜的一部分。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467da063.jpg) 参与者: 1. People,基类,提供一个模板方法,并且提供抽象的接口。模板方法调用接口完成具体的算法。 1. HighPeople、FatPeople,根据想要完成的情况,来具体实现抽象接口**。** 示例代码: ~~~ using System; using System.Collections.Generic; using System.Text; namespace Pattern23 { abstract class People { public abstract void Sort(); public abstract void Remove(); public void TemplateMethod() { Sort(); Remove(); } } class HighPeople : People { public override void Sort() { Console.WriteLine("按身高排序"); } public override void Remove() { Console.WriteLine("排除掉身高低于170cm的人"); } } class FatPeople : People { public override void Sort() { Console.WriteLine("按体重排序"); } public override void Remove() { Console.WriteLine("排除体量超过80K以及少于60KG的人"); } } class Program { static void Main(string[] args) { People people; people = new HighPeople(); people.TemplateMethod(); people = new FatPeople(); people.TemplateMethod(); } } } ~~~ 适用性: 1. 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。 1. 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。 首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。 最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。 1. 控制子类扩展。 优缺点: 1. 优点,完成新的模板方法是不用修改原有代码,通过添加新代码完成。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》
            ';

            22——行为型模式之策略模式

            最后更新于:2022-04-01 20:11:15

            **定义:**策略模式(Strategy Pattern),定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。 类型:对象行为型模式。 概述:       策略模式是比较典型的对象行为型模式,它是将对处理对象的一系列不同算法都单独抽离出来,单独封装成一个个类。策略的出现,主要是为了解决不同算法替换时的逻辑判断,将逻辑判断移到Client中去。策略模式比较常见,但是也相对比较简单。       一系列算法,作为程序员,很容易就想到排序算法。那这里就以排序算法来举例了。有文本内容Context,如果录得的是乱序数据,那么排序最好选择快速排序(Quick Sort);如果是多部分都有序,只有个别数据随意插入其中,那么此时可以使用选择排序(Select Sort)。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467b6109.jpg) 参与者: 1. Context,上下文,维护一个指向SortAlgo的指针,用来访问相应的排序算法。 1. SortAlgo,算法策略接口类,只是提供接口。 1. QuickSort、SelectSort,按照接口实现具体的算法。 示例代码: ~~~ using System; using System.Collections.Generic; using System.Text; namespace Pattern22 { // 抽象算法类 abstract class SortAlgo { //算法方法 public abstract void Sort(); } // 快速排序算法 class QuickSort : SortAlgo { // 具体实现快速排序算法 public override void Sort() { Console.WriteLine("快速排序算法实现"); } } // 选择排序算法 class SelectSort : SortAlgo { // 具体实现选择排序算法 public override void Sort() { Console.WriteLine("选择排序算法实现"); } } //上下文 class Context { SortAlgo algo; public Context(SortAlgo _algo) { this.algo = _algo; } //上下文接口 public void SortData() { algo.Sort(); } } class Program { static void Main(string[] args) { Context context; // 当录入的是随机数据时,选择使用快速排序算法 context = new Context(new QuickSort()); context.SortData(); // 当录入一些比较有顺序的数据时,使用选择排序算法 context = new Context(new SelectSort()); context.SortData(); Console.Read(); } } } ~~~ 适用性: 1. 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。 1. 在需要新添加其他算法来实现时。 1. 对客户端隐藏具体算法的实现细节。 优缺点: 1. 优点,将判断的逻辑移到类外面,方便通过添加新类来完成新的算法。 1. 缺点,如果算法类过多,会对Client端调用不同算法的判断带来麻烦。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》
            ';

            21——行为型模式之状态模式

            最后更新于:2022-04-01 20:11:13

            定义:状态模式(State Pattern),允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。 类型:对象行为型模式 概述:         一个对象有一个状态,那么每一个状态又对应一些相应的行为。如果这个对象有很多状态,那么就要对应非常多的行为。那么对这些状态的判断以及根据状态完成相应的行为,会非常复杂。并且如果想添加一种新的状态时,需要修改很多的现有代码。这也是有违开闭原则的。状态模式正是在这样一种情况下提出来的。        状态模式将每种状态对应的行为抽象出来成为单独的新的对象,这样将状态转换显式化了。状态的变换不再依赖于Context内部的行为了。另外,将状态及行为提出来能够大为降低Context对象的复杂度。另外如果一个State对应多个Context时,State还可以被多个Context对象共享。        状态,我们立马会提出,今天状态不好,做什么都没劲;又或者是今天状态很好,做事得劲,饭也吃得多。那么我们就以一个人不同时刻的状态为例来讲解状态模式。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467964dc.jpg) 参与者: 1. Human,也即Context通过抽象接口来调用状态对象的具体实现。 1. State,封装了与Human相关行为的接口。 1. Happy,Sad,具体实现了与相应状态下的行为。 示例代码: ~~~ using System; using System.Collections.Generic; using System.Text; namespace Pattern21 { //抽象状态 public abstract class State { public abstract void Eat(); public abstract void Walk(); } // 高兴时的状态 public class Happy : State { public override void Eat() { human.Eat(); Console.WriteLine("很多!"); } public override void Walk() { human.Walk(); Console.WriteLine("手舞足蹈的!"); } public void Attach(Human _human) { human = _human; } private Human human; } // 伤心时的状态 public class Sad : State { public override void Eat() { human.Eat(); Console.WriteLine("很少!"); } public override void Walk() { human.Walk(); Console.WriteLine("无精打采的!"); } public void Attach(Human _human) { human = _human; } private Human human; } // 一个人 public class Human { private State current; public void SetState(State s) { current = s; } public void Eat() { Console.Write("吃了"); } public void Walk() { Console.Write("走起路来"); } public void Show() { current.Eat(); current.Walk(); } } class Program { static void Main(string[] args) { // 定义一个有很多状态的对象 Human human = new Human(); // 定义一个高兴的状态 Happy hState = new Happy(); hState.Attach(human); human.SetState(hState); human.Show(); // 定义一个伤心的状态 Sad sad = new Sad(); sad.Attach(human); human.SetState(sad); human.Show(); // 还可以添加生病的状态,只需要添加新的类而不需要修改Human类 // ...... Console.Read(); } } } ~~~ 适用性: 1. 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。 1. 一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。 优缺点: 1. 优点,将状态判断的逻辑移到类外面,方便通过添加新类来添加新的状态。 1. 缺点,如果状态非常多,会导致有非常多的状态类,加大开销。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》
            ';

            20——行为型模式之观察者模式

            最后更新于:2022-04-01 20:11:11

            **定义:**观察者模式(ObserverPattern),定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 类型:对象行为型模式 概述:         观察者模式也是比较难以理解,单看类图,会发现观察者模式与中介模式非常相似。如果不真正理解他们二者之间的区别,是难以真正理解观察者模式的。如果不理解观察者模式,更奢谈运用观察者模式了。         中介模式主要是解决一系列交互对象的耦合的问题,而提出了中介类。几个以前交互的对象现在统一通过中介类来完成交流。而观察者模式侧重的是一对多的通知的功能,即当发生某件事时,主题对象将向所有注册过的观察者对象发出通知消息。         中介模式侧重通过中介类来完成交流;而观察者模式侧重于一对多的通知。中介模式不需要知道具体的中介类的对象,需要交互的对象只需要知道一个通知中介对象的接口即可,具体是哪个中介来完成交互对象告诉的任务,交互对象不需要知道。但是交互对象必须知道有哪些交互对象,这样才能把对象A需要对象B做的事想办法通知到对象B。          观察者模式则,Subject对象不需要知道具体的观察者是谁,它只需要知道观察者的通知接口,就可以将消息通知到观察者。至于具体有哪些消息,则需要观察者自己去具体的Subject那里查看。          这里举一个例子,某电商有大促销,通过邮件发送促销消息给消费者。但是如果消费者想了解有哪些具体的促销信息,则即需要亲自去电商网站查看有哪些具体的促销信息。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b467733f3.jpg) **参与者:** 1. Vender,提供接口来完成对观察者的注册通知。 1. YiXun,具体的电商,为消费者提供所需要查看具体信息。 1. Observer,提供Update接口。 1. CustomerA、CustomerB,具体的观察者,当有更新时去查看电商中自己喜欢的产品。 **示例代码:** ~~~ using System; using System.Collections.Generic; using System.Text; namespace Design20 {    abstract class Vender    {        private IList observers = new List();        // 观察者注册        public void Register(Observer observer)        {            observers.Add(observer);        }        //通知        public void Notify()        {            foreach (Observer o in observers)            {                o.Update();            }        }    }    //具体通知者    class YiXun : Vender    {        private string subjecPhone;        private string subjecLaptop;        //具体通知者状态        public string SubjecPhone        {            get { return subjecPhone; }            set { subjecPhone = value; }        }        //具体通知者状态        public string SubjecLaptop        {            get { return subjecLaptop; }            set { subjecLaptop = value; }        }    }    abstract class Observer    {        public abstract void Update();    }    class CustomerA : Observer    {        private YiXun subject;        public CustomerA(          YiXun subject)        {            this.subject = subject;        }        //更新        public override void Update()        {            Console.WriteLine("消费者A查看到的是{0}", this.subject.SubjecPhone);        }        public YiXun Subject        {            get { return subject; }            set { subject = value; }        }    }    class CustomerB : Observer    {        private YiXun subject;        public CustomerB(          YiXun subject)        {            this.subject = subject;        }        //更新        public override void Update()        {            Console.WriteLine("消费者B查看到的是{0}", this.subject.SubjecLaptop);        }        public YiXun Subject        {            get { return subject; }            set { subject = value; }        }    }    class Program    {        static void Main(string[] args)        {            YiXun subject = new YiXun();            subject.Register(new CustomerA(subject));            subject.Register(new CustomerB(subject));            // 来了新手机和新笔记本电脑,然后通知到所有注册的消费者.            // 然后消费者去查看自己感兴趣的产品有哪些.            subject.SubjecPhone = "IPhone5s";            subject.SubjecLaptop = "ThinkPad";            subject.Notify();            Console.Read();        }    } } ~~~ **适用性:** 1.  一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。 1. 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。 1. 想定制一个分布在多个类中的行为,而又不想生成太多的子类。 优缺点: 1. 优点,降低命令发起者与命令接受者之间的耦合,发布者通过接口通知所有观察者。 1. 缺点,具体的观察者依赖了具体的发布者。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》
            ';

            19——行为型模式之备忘录模式

            最后更新于:2022-04-01 20:11:08

             定义:备忘录模式(MementoPattern),在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

            类型:对象行为型模式。

            概述:

                      备忘录模式使用的范围比较窄,即比较复杂的状态需要恢复到之前的状态。现实更多的情况只有很简单的状态需要恢复,如果用备忘录模式有点大材小用的感觉。就像用大炮打蚊子,起不到什么作用。在《Head First Design Patterns》,备忘录模式也被列为不常用的模式之一。

                     备忘录模式相对比较简单,即把一个有着复杂状态且需要恢复Originator的对象中的一些需要被保存的状态再次抽象出来,并提供一些操作接口封装成一个Memento类。这样将极大简化Originator类的复杂性。Memento的接口因为是操作状态的,所以不应该开放给更多的人知道,所以这里又继续封装了一个管理Memento类的Caretaker类,并提供一些简单对外的接口。

                    恢复状态,其实玩过单机剧情类游戏的人都会马上想到“存档”这个功能。“存档”功能在某种意义上就是备忘录的一种应用。这里即以我最喜欢的游戏Warcraft III为示例。

            类图:

            参与者:

            1. Warcraft,也即Originator,它的状态需要被记录以及恢复。
            1. Memento,抽象出Warcraft的内部状态,并提供操作方法。
            1. Caretaker,管理Memento,并向外提供记录及恢复状态的方法,但是不了解具体的执行过程。

            示例代码:

            using System;
            using System.Collections.Generic;
            using System.Text;
            namespace Pattern19
            {
                class Warcrift
                {
                    private string role;
                    public string Role
                    {
                        get { return role; }
                        set { role = value; }
                    }
                    public Memento CreateMemento()
                    {
                        return (new Memento(role));
                    }
                    public void SetMemento(Memento memento)
                    {
                        role = memento.Role;
                    }
                    public void Start()
                    {
                        Console.WriteLine("There is a " + role);
                    }
                }
                class Memento
                {
                    private string role;
                    public Memento(string _role)
                    {
                        this.role = _role;
                    }
                    public string Role
                    {
                        get { return role; }
                    }
                }
                class Caretaker
                {
                    private Memento memento;
                    public Memento Memento
                    {
                        get { return memento; }
                        set { memento = value; }
                    }
                }
                class Program
                {
                    static void Main(string[] args)
                    {
                        // 初始角色是英雄
                        Warcrift war3 = new Warcrift();
                        war3.Role = "Hero";
                        war3.Start();
                        Caretaker caretaker = new Caretaker();
                        caretaker.Memento = war3.CreateMemento();
                        // 更换角色为小兵
                        war3.Role = "Soldier";
                        war3.Start();
                        // 退回到初始状态
                        war3.SetMemento(caretaker.Memento);
                        war3.Start();
                    }
                }
            }
            适用性:
            1. 必须保存一个对象在某一个时刻(部分)状态这样以后需要它时才能恢复到先前的状态。
            1. 如果一个用接口来让其它对象直接得到这些状态,将会暴露对象的实现细节并破坏对象的封装性。

            优缺点:

            1. 优点,降低对象之间的耦合度,还降低管理对象的复杂度。
            1. 缺点,如果需要存储的信息非常多,将会赞成非常大的开销。

            参考资料:

            1. 《设计模式——可复用面向对象软件基础》
            1. 《大话设计模式》


            ';

            18——行为型模式之中介者模式

            最后更新于:2022-04-01 20:11:06

            **定义:**中介者模式(MediatorPattern),用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 **类型:**对象行为型模式 概述:          面向对象编程,鼓励将行为分散到对象当中。这样的好处,增强对象的可复用性,但是带来的负面作用是,当多个对象交互时不可避免地会带来耦合性的大大提升。          中介者模式的提出,就是为了解决多个对象相互交互时耦合性提升的问题。中介者模式主要通过将不同对象中涉及到交互的行为抽象出来,达到解耦的作用。这样,每个对象都只需要和中介者打交到即可,不用关注太多的对象。         比如一个项目组,有一个项目经理和几个项目开发人员。如,开发人员需要开发资料,直接找项目经理要,项目经理去协调采购人员进行采购。又如,开发人员需要了解一个具体的需求,也直接找项目经理,然后项目经理去找销售人员了解更详细的需求。总之,开发人员,需要什么,都直接找项目经理就可以了,而不必关心去找谁,并如何跟对象交流。这样看来,开发人员就不会和其他对象产生交互,也就不会有很大的耦合性,比较纯粹,专注于自己的开发就好。这样,只要技术过关的人都可以充当开发人员,而不必要有各种关系,沟通技巧之类的。提高了开发人员的可复用性。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b4674cffa.jpg) 参与者: 1. Mediator,提供与Staff交互的接口。 1. ProjectManger,实现具体的接口来完成和不同职员的交互。 1. Staff,提供成员与Mediator交互的基本接口。 1. Developer,Buyer,Seller,完成各自的工作,然后通过Meditor的接口告诉具体的ProjectManager。 示例代码: ~~~ using System; using System.Collections.Generic; using System.Text; namespace MediatorPattern { abstract class Mediator { public abstract void Comunicate(string message, Staff colleague); } class ProjectManger : Mediator { private Developer staff1; private Buyer staff2; private Seller staff3; public Developer Staff1 { set { staff1 = value; } } public Buyer Staff2 { set { staff2 = value; } } public Seller Staff3 { set { staff3 = value; } } public override void Comunicate(string message, Staff staff) { staff.Notify(message); } } abstract class Staff { protected Mediator mediator; public Staff(Mediator mediator) { this.mediator = mediator; } public abstract void Notify(string message); } class Developer : Staff { public Developer(Mediator mediator) : base(mediator) { } public void Ask(string message) { mediator.Comunicate(message, this); } public override void Notify(string message) { Console.WriteLine("开发人员完成开发任务" + message); } } class Buyer : Staff { public Buyer(Mediator mediator) : base(mediator) { } public void Send(string message) { mediator.Comunicate(message, this); } public override void Notify(string message) { Console.WriteLine("采购人员已经知道" + message); } } class Seller : Staff { public Seller(Mediator mediator) : base(mediator) { } public void Tell(string message) { mediator.Comunicate(message, this); } public override void Notify(string message) { Console.WriteLine("销售人员已经知道" + message); } } class Program { static void Main(string[] args) { ProjectManger mediator = new ProjectManger(); Developer staff1 = new Developer(mediator); Buyer staff2 = new Buyer(mediator); Seller staff3 = new Seller(mediator); mediator.Staff1 = staff1; mediator.Staff2 = staff2; mediator.Staff3 = staff3; staff1.Ask("需要电路板"); staff2.Send("已经采购电路板"); staff1.Ask("需要客户需求"); staff3.Tell("已经去调查"); } } } ~~~ **适用性:** 1. 一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。 1. 一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。 1. 想定制一个分面在多个类中的行为,而又不想生成太多的子类。 优缺点: 1. 优点,降低对象之间的耦合度,提高对象的可复用用程度。 1. 缺点,如果交互的对象过多,中介者会非常复杂,难于管理。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》
            ';

            17——行为型模式之迭代器模式

            最后更新于:2022-04-01 20:11:04

            **定义:**迭代器模式(IteratorPattern),提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。 类型:对象行为型模式 **概述:**     正如定义所言,迭代器是为解决更好的访问一个聚合对象中的各个元素而产生的。迭代器是对象行为模式,也即它主要是通过将对象中的访问行为提取出来作为迭代器类。迭代器的产生,主要是为了解决客户端对聚合对象内部元素访问时可能造成的耦合。         为了使得迭代器能够访问更多的聚合对象,所以需要面向接口编程,即为迭代器和聚合对象分别提供统一接口。          C++中的STL库、C#,Java的泛型类型都有封装迭代器,所以下面的示例重点了解迭代器的工作原理。         举一个简单示例,有一个图书馆,存有许多图书。一天,有读者来借书,这个时候就可以使用迭代器模式了。迭代器将一系列图书的遍历查找定位等操作抽象出来形成一个迭代器类。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b4672698f.jpg) 参与者: 1. Client,生成具体的NationalLibrary以及具体的ConcreteIterator对象。 1. Library,提供聚合对象的抽象接口。 1. NationalLibrary,实现接口,并返回一个ConcreteIterator的对象。 1. Iterator,迭代器接口类。 1. ConcreteIterator,完成接口实现遍历查找。 示例代码: ~~~ using System; using System.Collections.Generic; using System.Text; namespace IteratorSpace { class Program { abstract class Library { public abstract Iterator CreateIterator(); } class NationalLibrary : Library { private IList items = new List(); public override Iterator CreateIterator() { return new ConcreteIterator(this); } public int Count { get { return items.Count; } } public object this[int index] { get { return items[index]; } set { items.Insert(index, value); } } } abstract class Iterator { public abstract object First(); public abstract object Next(); public abstract bool End(); public abstract object CurrentItem(); } class ConcreteIterator : Iterator { private NationalLibrary library; private int current = 0; public ConcreteIterator(NationalLibrary _library) { this.library = _library; } public override object First() { return library[0]; } public override object Next() { object ret = null; current++; if (current < library.Count) { ret = library[current]; } return ret; } public override object CurrentItem() { return library[current]; } public override bool End() { return current >= library.Count ? true : false; } } static void Main(string[] args) { NationalLibrary library = new NationalLibrary(); library[0] = "C++ Primer"; library[1] = "C Program"; library[2] = "Design Pattern"; Iterator iter = new ConcreteIterator(library); while (!iter.End()) { Console.WriteLine("This book is {0}!", iter.CurrentItem()); iter.Next(); } } } } ~~~ 适用性: 1. 访问一个聚合对象的内容而无需暴露它的内部表示。 1. 支持对聚合对象的多种遍历。 1. 为遍历不同的聚合结构提供一个统一的接口(即,支持多态迭代) 优缺点: 1. 优点,简化遍历过程,降低客户端与聚合对象之间的耦合性。 1. 缺点,如果是简单聚合对象使用迭代器反倒复杂,另外新添聚合类以及迭代器类过多时,也会造成类过多而复杂 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》 1. 《Head First设计模式》 ';

            16——行为型模式之解释器模式

            最后更新于:2022-04-01 20:11:02

            **定义:**解释器模式(Interpreter Pattern)给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 **类型:**类行为型模式 概述:          正如GoF所介绍的,解释器模式的应用场合比较窄。解释器模式在《Head First Design Pagtterns》中也被列为非常用模式。解释器模式虽然说更多应用在某种语言上,但是解释器的原理还是可以应用在其他场合的。           解释器模式与组合模式有很大的相似性。正如GoF所言,在最宽泛的概念下,几乎每个使用复合模式的系统也都使用了解释器模式。解释器模式与组合模式的不同主要体现在,解释器模式中的几个解释器类都有相同的接口,而组合模式则是直接聚合几个没有关联的类。          解释器模式虽说多用于某种解释语言上,但是其思想还是可以应用在其它方面的。解释器模式作为类行为模式,其主要是通过将一组平行操作的行为抽象出来作为独立的类存在。以达到降低类调用的耦合以及更方便地添加新的解释操作。这里举一个比较简单示例,有一段文字内容,需要用几个解释器来识别其中的地名,人名。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b466e8279.jpg) ![](image/d41d8cd98f00b204e9800998ecf8427e.jpg) 参与者: 1. Client,构建Context,以及完成解释器的调用操作。 1. Context,需要被解释的文件内容。 1. Expression,抽象类,提供接口。 1. NameExpression、AddressExpression,实现接口的具有解释操作。            示例代码: ~~~ using System; using System.Collections.Generic; using System.Text; namespace Interpreter { class Context { private string input; public string Input { get { return input; } set { input = value; } } private string output; public string Output { get { return output; } set { output = value; } } } abstract class AbstractExpression { public abstract void Interpret(Context context); } class NameExpression : AbstractExpression { public override void Interpret(Context context) { Console.WriteLine("名字解释器"); } } class AddressExpression : AbstractExpression { public override void Interpret(Context context) { Console.WriteLine("地址解释器"); } } class Program { static void Main(string[] args) { Context context = new Context(); IList list = new List(); list.Add(new NameExpression()); list.Add(new AddressExpression()); foreach (AbstractExpression exp in list) { exp.Interpret(context); } } } } ~~~ 适用性: 1. 该文法简单对于复杂的文法,文法的类层次变得庞大而无法管理。 1. 效率不是一个关键问题最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。 优缺点: 1. 优点,能够灵活添加新的操作类。 1. 缺点,使用范围过于狭窄。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》 1. 《Head First设计模式》
            ';

            15——行为型模式之命令模式

            最后更新于:2022-04-01 20:10:59

            **定义:**命令模式(Command Pattern),将一个请求封闭成一个对象,从而使你可用不同的请求对客户进行参数化;对请求排除或记录请求日志,以及支持可撤销的操作。 **类型:**行为型模式。 **概述:**          命令模式,被《Head First设计模式》列为常见的设计模式之一,而且光看类图,感觉很简单。可是真正要去运用的时候,往往会发现无从下手。因为你不知道什么时候该使用命令模式。任何关联的两个类,你发现居然都可以使用命令模式。但是使用了之后,发现代码理解起来似乎变得更复杂了。本来比较简单,并且耦合并不严重的两个类,再加进来一个命令类,反倒更复杂了。如果严格依照迪米特法则,差不多任何两个有关联的类都似乎需要一个中介类来减少它们的耦合。如果极端地这样做,必将出现非常多的中介类,反倒将程序弄得更复杂了。         在代码发展的初期,我们很难发现需要使用哪种模式。其实这个时候,我们也不必纠结该使用哪种模式,我们先把基本的对象抽象好就行了。待代码发展到比较复杂开始需要重构代码时,这个时候就是设计模式上场的时候了。重构代码时,发现两个类严重耦合,这个时候就得考虑使用何种方式解耦了。         创建型模式和结构型模式,更多地考虑代码的复用,而行为型模式更多地考虑代码的解耦。命令模式的作用,就是解耦的,主要解决那些“发出命令”和“响应命令”两者之间的耦合。所谓术业有专攻,这句话在软件设计体现得淋漓尽致。每个人都做自己擅长的事情,把不擅长的事情独立出来,交给专门的人来做。        因为命令模式是抽象出命令的, 所以那些需要处理多次命令的,例如记录,删除,查找等操作命令的时候,就可以使用命令模式。        到处是点菜下订单这样的示例,虽然这是个非常好的应用命令模式的示例,但是被大家过多讲述。所以这里,我举一个新的示例。小机床作坊,老板(Boss)加10位操作工(A~J)的干活,主要生产螺丝、轴承等等。 **类图:** **![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b466c3f6e.jpg)** 参与者: 1. Client,创建Boss、Workers以及具体的Command。 1. Boss,也即Invoker,调用命令。 1. ScrewCMD,BearingCMD,具体的命令,告诉Workers如何相应的生产。 1. Workers,也即Receiver,接收命令并响应具体的命令。 示例代码: ~~~ 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 5 namespace CMD 6 { 7 abstract class Command 8 { 9 protected Workers workers; 10 11 public Command(Workers _workers) 12 { 13 this.workers = _workers; 14 } 15 16 abstract public void Execute(); 17 } 18 19 class ScrewCMD : Command 20 { 21 public ScrewCMD(Workers _workers) : base(_workers) { } 22 23 public override void Execute() 24 { 25 workers.ProductScrew(); 26 } 27 } 28 29 class BearingCMD : Command 30 { 31 public BearingCMD(Workers _workers) : base(_workers) { } 32 33 public override void Execute() 34 { 35 workers.ProductBearing(); 36 } 37 } 38 39 class Workers 40 { 41 public void ProductScrew() 42 { 43 Console.WriteLine("生产螺丝!"); 44 } 45 46 public void ProductBearing() 47 { 48 Console.WriteLine("生产轴承."); 49 } 50 } 51 52 class Boss 53 { 54 private Command command; 55 56 public void SetCommand(Command command) 57 { 58 this.command = command; 59 } 60 61 public void ExecuteCommand() 62 { 63 command.Execute(); 64 } 65 } 66 67 class Program 68 { 69 static void Main(string[] args) 70 { 71 Workers workers = new Workers(); 72 Command screw = new ScrewCMD(workers); 73 Command bearing = new BearingCMD(workers); 74 Boss boss = new Boss(); 75 76 // 生产螺丝的命令 77 boss.SetCommand(screw); 78 boss.ExecuteCommand(); 79 80 // 生产轴承的命令 81 boss.SetCommand(bearing); 82 boss.ExecuteCommand(); 83 84 Console.Read(); 85 } 86 } 87 } ~~~ 适用性: 1. 抽象出待执行的动作以参数化某对象。 1. 在不同的时刻指定、排列和执行请求。 1. 支持取消操作 1. 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。 1. 用构建在原语操作上的高层操作构造一个系统。 优缺点: 1. 优点,降低命令发起者与命令接受者之间的耦合,将复杂的命令过程独立出来。 1. 缺点,如果命令过多,管理命令本身会变得复杂起来。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》 1. 《Head First设计模式》
            ';

            14——行为型模式之职责链模式

            最后更新于:2022-04-01 20:10:57

            **定义:**职责链模式(Chain OfResponsibility Pattern),使从个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理安为止。 类型:行为型模式。 概述:        职责链模式,行为型模式,说明其主要是通过行为来表现的。这里的行为,也可以理解为方法(Method)。职责链模式,将一堆处理事件的行为对象抽象为一个个的类。有些类似链表结构,每个类中都有一个指针,指向它的下一个类。这样,当一个类处理完它要处理的事情之后,就将工作将给下一个类来处理。如果下一个类发现它暂时无事可做,它依然将职责转到下一个类去处理。        职责链模式,好将每一个职责抽象成一个类。然后处理某件事,会有一系列的职责者,这样就抽象出一系列的职责。然后用指针,像链表一样,将它们链起来,就形成了职责链模式了。         一系列的职责,抽象出相同的接口,以及一个指针形成抽象类作为这一系列职责类的抽象基类。        例如,生产线的工人。每个人,针对生产产品的职责,去完成自己的职责,完成之后,就可以交给生产线上的下一位员工。有些生产线,对员工职责的顺序有要求,有些生产线没有要求。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b466a4d9e.jpg) 参与者: 1. Client,发起职责。 1. Worker,抽象基类,提供所有其他具体工人工作的接口,另外有一个指针,指向Worker类的变量。 1. Installer、Packer、QC,生产线上的三种工人,分别实现自己要做的工作,另外指向它的下一个职责者,如果没有,则不用指出。 示例代码: ~~~ using System; using System.Collections.Generic; using System.Text; namespace Design14 { // 抽象接口 abstract class Worker { public Worker worker; public abstract void DoHandler(); } class Installer : Worker { public Installer(Worker _worker) { this.worker = _worker; } public override void DoHandler() { Console.WriteLine("完成产品的基本组装"); if (null != this.worker) { this.worker.DoHandler(); } } } class Packer : Worker { public Packer(Worker _worker) { this.worker = _worker; } public override void DoHandler() { Console.WriteLine("完成产品的打包"); if (null != this.worker) { this.worker.DoHandler(); } } } class QC : Worker { public QC(Worker _worker) { this.worker = _worker; } public override void DoHandler() { Console.WriteLine("完成产品的质检"); if (null != this.worker) { this.worker.DoHandler(); } } } // Client class Program { static void Main(string[] args) { // 完成职责链的构建 Worker qc = new QC(null); Worker packer = new Packer(qc); Worker installer = new Installer(packer); // 开户执行职责 installer.DoHandler(); } } } ~~~ 适用性: 1. 对于同一请求,有多个不同的处理方式。 1. 有多个请求,不知道哪一个请求正确。 注意: 1. 如果是C++,抽象类一定要注意析构函数一定要是虚函数。 1. 职责链并不一定要求是一条链条,也可以是像树一样. 优缺点: 1. 优点,降低了请求者与响应者之间的耦合度。增强了职责的灵活性,可以不同职责组合。 1. 缺点,当职责链比较多时,对于链的正确配置容易遗漏。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》 1. 《Head First设计模式》
            ';

            13——结构型模式之代理模式

            最后更新于:2022-04-01 20:10:55

            **定义:**代理模式(ProxyPattern),为其他对象提供一种代理以控制对这个对象的访问。 类型:结构型模式 分类: 1. 远程代理,为一个对象在不同的地址空间提供局部代表。 1. 虚代理,根据需要创建开销很大的对象。 1. 保护代理,控制对原始对象的访问。 1. 智能指引,取代简单指针,在访问时执行一些附加操作。 概述: 1. 远程代理,如代理商,什么区域代、省代之类的都算是远程代理。代理商在某一局部代表厂商。     1. 虚代理,主要是为了解决一次开销过大的情况,例如打开大的文件之类的。 1. 保护代理,律师就是代理委托人行使其保护其权利,正好是保护代理。 1. 智能指针,智能指针,画图双缓存等都算是智能指引了,主要是复用对象的某些特性自动完成某些工作,如构造函数、析构函数之类的。         无论是哪一种代理,都是一个代理对象来代理完成被代理的工作,当然这期间代理对象会添加一些功能使得客户更方便地操作。为了让客户操作代理对象像操作原对象一样方便,代理类一般会保持和原对象相同的接口。上面四种代理模式,只是一些经常被用到的代理功能。实际的使用中,并不一定需要拘泥于这四种代理模式。一切为了让用户更方便更快捷地操作对象而产生的中间类都算是代理模式的运用。代理模式其实很简单,就是一个代理类对另一个类的再封装,以便更好的让客户调用。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b4667ffdf.jpg) 参与者: 1. Client,客户端提供交互。 1. Subject,提供接口。 1. RealSubject,完成接口,是真正需要被代理的类。 1. Proxy,有一个Subject的指针,指向RealSubject。调用RealSubject.DoSomething()实现DoSomething接口,并扩展一些功能。 示例代码: ~~~ 1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 5 namespace Design13 6 { 7 // 主要是提供接口 8 abstract class Subject 9 { 10 public abstract void DoSomething(); 11 } 12 13 // 具体的对象 14 class RealSubject : Subject 15 { 16 public override void DoSomething() 17 { 18 Console.WriteLine("做一些事"); 19 } 20 } 21 22 // 代理类,和具体的对象实现的是相同的接口 23 class Proxy : Subject 24 { 25 private Subject realSubject; 26 public Proxy() 27 { 28 realSubject = new RealSubject(); 29 } 30 public override void DoSomething() 31 { 32 if (realSubject != null) 33 { 34 realSubject.DoSomething(); 35 Console.WriteLine("再做一些其他事"); 36 } 37 } 38 } 39 40 // Client 41 class Program 42 { 43 static void Main(string[] args) 44 { 45 Proxy proxy = new Proxy(); 46 proxy.DoSomething(); 47 } 48 } 49 } ~~~ **注意:**如果是C++,抽象类一定要注意析构函数一定要是虚函数。 与其他模式比较:代理模式与装饰者模式有一定的类似度。但是两者目的不一样,代理模式是为了让客户更方便地访问。而装饰者模式侧重于添加新的功能。 优缺点: 1. 优点,减少内存的使用。 1. 缺点,加大了结构的复杂度,需要单独管理外部状态。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》 1. 《Head First设计模式》
            ';

            12——结构型模式之享元模式

            最后更新于:2022-04-01 20:10:53

            **定义:**享元模式(Flyweight Pattern),运用共享技术有效地支持大量细粒度的对象。 类型:结构型模式。 适用情况: 1. 一个应用程序有大量的对象。 1. 对象的大多数状态都是外部的。 1. 如果删除对象的外部状态,可以和相对较少的共享对象取代很多组对象。 1. 应用程序不依赖对象的标识,即应用程序依赖于对象的抽象接口。 **概述:**        Flyweight,原意为“轻量级选手”的意思。翻译者将它意为享元模式,是意译,力求能够直观地表现出此模式的目的。享,共享之意。元,基本单元的意思。享元,也就是共享基本单元,也即GoF所言的运用共享技术有效地支持大量细粒度的对象。        享元模式的重点在于将对象的“内部状态”和“外部状态”抽象出来,内部状态存储在享元对象中,而外部状态在外部存储。这个才是享元模式的关键,网上很多文章根本没有讲透这一点。         下面举一个示例。有一棋盘上的围棋正好是大量细粒度的对象。我们将材质、形状、制作工艺抽象为内部状态, 而围棋在棋盘上的位置是标识每一棋子的标记,故棋子的位置抽象为外部状态。而棋子只有白黑两种颜色,如果存储在外部状态里,会存两种颜色很多次。故这里,白棋、黑棋作为两个对象存在。 类图**:** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b46665ffa.jpg) 参与者: 1. Client,调用ChessBoard得到具体的棋子对象。 1. Chessboard,管理棋子的生成以及存储棋子的外部状态,即每个棋子的具体位置。 1. Weiqi,围棋抽象类,即享元对象,抽象出一些内部状态来。 1. WhiteWeiqi、BlackWeiqi,两个派生出的不同颜色的围棋,实现颜色接口。 示例代码: // Flyweight类 ~~~ public abstract class Weiqi { // 内部状态 // ............... // private int nSize; public abstract Color GetColor(); } ~~~ // 具体的Flyweight类 ~~~ public class WhiteWeiqi : Weiqi { public override Color GetColor() { Console.WriteLine("White"); return Color.White; } } public class BlackWeiqi : Weiqi { public override Color GetColor() { Console.WriteLine("Black"); return Color.Black; } } ~~~ // FlyweightFactory类 ~~~ public class Blessboard { private List listWhite = new List(); private List listBlack = new List(); private WhiteWeiqi wWeiqi; private BlackWeiqi bWeiqi; public Blessboard() { wWeiqi = new WhiteWeiqi(); bWeiqi = new BlackWeiqi(); } public Weiqi Produce(bool bWthite, Point pt) { if (bWthite) { listWhite.Add(pt); return bWeiqi; } else { listBlack.Add(pt); return bWeiqi; } } public Weiqi GetProduce(Point pt) { foreach (Point p in listWhite) { if (p.Equals(pt)) { return wWeiqi; } } foreach (Point p in listBlack) { if (p.Equals(pt)) { return bWeiqi; } } return null; } } ~~~ // Client类 ~~~ class Program { static void Main(string[] args) { Blessboard bloard = new Blessboard(); // 生成棋子 bloard.Produce(true, new Point(1, 3)); bloard.Produce(false, new Point(2, 3)); bloard.Produce(true, new Point(1, 4)); bloard.Produce(false, new Point(2, 4)); // 查询棋子黑白 Weiqi weiqi = bloard.GetProduce(new Point(2, 4)); Color color = weiqi.GetColor(); weiqi = bloard.GetProduce(new Point(1, 4)); color = weiqi.GetColor(); } } ~~~ **注意:**如果是C++,抽象类一定要注意析构函数一定要是虚函数。 优缺点: 1. 优点,减少内存的使用。 1. 缺点,加大了结构的复杂度,需要单独管理外部状态。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》 1. 《Head First设计模式》
            ';

            11——结构型模式之外观模式

            最后更新于:2022-04-01 20:10:50

            **定义:**外观模式(Façade Pattern),为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 类型:结构型模式。 适用性: 1. 当子系统非常复杂时,使得客户调用非常麻烦,不便于使用。这个时候就可以使用外观模式将这些子系统封装起来,提供一个统一而简单接口供客户端使用。 1. 当需要构建一个层次结构的子系统时,使用外观模式可以给子系统定义进入口。 概述:          外观模式主要依赖“迪米特法则”,即尽量减少与其他实体之间的联系。例如,用户和一台电脑。如果用户想开机,他不需要云单独打开电源,再启动CPU,再启动硬盘电源,再连接显示器。用户不需要知道电脑开机完成了哪些工作,用户只想开机了就可以正常使用电脑就行了。用户要做的,只是按一下开机键,所有的一切都会在这一个开机动作中自动完成。减少用户对电脑内部的了解,这样能够使得用户更方便地使用电脑,而且如果电脑开机有问题,也不需要云从用户身上找,直接去电脑上面找就可以,能够更方便地定位问题。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b466414b1.jpg) 参与者: 1. Client,客户端,直接与Computer交互。 1. Computer,由Display,Power,HardDisk几个子系统构成。并且提供一个Startup的接口用来一次性控制所有子系统启动。 1. Display,Power,HardDisk,子系统,完成Computer指派的功能。 示例代码: ~~~ // C# namespace Design11 {    class Power    {        public void Start()        {            Console.WriteLine("启动电源");        }    }    class Display    {        public void DisplayContent()        {            Console.WriteLine("显示内容");        }    }    class HardDisk    {        public void ReadDisk()        {            Console.WriteLine("读取硬盘数据");        }    }    // Fasade    class Computer    {        private Power power = new Power();        private Display display = new Display();        private HardDisk hDisk = new HardDisk();        public void Startup()        {            power.Start();            display.DisplayContent();            hDisk.ReadDisk();        }    }    // Client    class Program    {        static void Main(string[] args)        {            Computer computer = new Computer();            Console.WriteLine("准备启动电脑");            computer.Startup();        }    } } ~~~ 优点:使得代码更加模块化,更方便调用以及查找问题。 **注意:**如果有多个子系统需要进行通讯时,最好是通过Facade来中转。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《大话设计模式》 1. 《Head First设计模式》
            ';

            10——结构型模式之装饰者模式

            最后更新于:2022-04-01 20:10:48

            定义:动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。 类型:结构型模式。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b46621286.png) 参入者: 1. Component,抽象类,只是提供接口。 1. Model,一个具体对象,实现接口完成一些基本的功能。 1. Decorator,有一个指向Component的指针,并且实现了Componet的接口。 1. Hairtician,Costumer,都是具体的装饰者,首先在实现基本的接口下,添加了新的功能。 适用性: 1. 装饰模式其应用体现在装饰上。那些需要动态添加同类型功能的产品,都可以使用装饰模式。 1. 以添加新类的方式来给单个对象添加新装饰。 1. 当不能以子类的方式扩展时,可以使用装饰模式来扩展。 概述:          装饰者模式是一种结构模式,表明装饰者模式主要是通过更改类的结构关系来实现的。Decorator与Model保持相同的接口,主要是为了保证操作Hairtician和Costumer时保持和操作Model时一样的体验。理论上来说,Decorator和Model不保持一样的接口,重新定义接口也是可以的。这里的装饰是一种链式的,即每一种新添加的装饰都是在之前的基本上生成新的类来完成的。新类在完成新添加的装饰之后,然后将接下来的动作返还到上一个装饰者。依次这样,达到链式完成装饰的功能。因为保持接口一致,在客户看来,其实是一致的,不影响客户的调用及体验。 示例代码: ~~~ // C# namespace Design10 {    class Component    {        public virtual void MakeUp() { }    }    class Model : Component    {        public override void MakeUp()        {            Console.WriteLine("模特自己完成基本的装扮");        }    }    abstract class Decorator : Component    {        protected Component component;        public void SetComponent(Component component)        {            this.component = component;        }        public override void MakeUp()        {            if (component != null)            {                component.MakeUp();            }        }    }    class Hairtician : Decorator    {        public override void MakeUp()        {            base.MakeUp();            Console.WriteLine("做新的发型");        }    }    class Costumer : Decorator    {        public override void MakeUp()        {            base.MakeUp();            AddedBehavior();        }        private void AddedBehavior()        {            Console.WriteLine("穿个性化的裙子");        }    }    class Program    {        static void Main(string[] args)        {            Model model = new Model();            Decorator decorator1 = new Hairtician();            Decorator decorator2 = new Costumer();            decorator1.SetComponent(model);            decorator2.SetComponent(decorator1);            decorator2.MakeUp();        }    } } ~~~ 与其他模式比较:装饰者模式其实与组合模式有一些类似的地方。都是通过组合的方法来实现添加新的模块。只是装饰者模式将产品独立出来,抽象出来装饰这个动作。而组合模式没有将产品与动作分开,组合模式的链条可以是像装饰者模式模式那样单一链条,也可是树形的,分出多个分支。 注意: 1. 如果是C++代码需要注意抽象类的析构函数必须是虚函数,否则释放内存的时候会出现问题。 1. C#,Java的时候,最开始的接口类不能改为接口,这样多态在经过多次继承之后会出现问题。 优缺点: 1. 优点,动态添加新功能,能够组合不同的功能,并且保证接口的一致。 1. 缺点,如果添加的功能过多,会增加过多的类,增加程序复杂度。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《Java与模式》 1. 《大话设计模式》 1. 《Head First设计模式》
            ';

            9——结构型模式之组合模式

            最后更新于:2022-04-01 20:10:46

            **定义:**组合模式(CompositePattern),将对象组合成树形结构以表示“部分—整体”的层次结构。“Composite”使得用户对单个对象和对组合对象的使用具有一致性。 类型:结构型模式。 类图:![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b465c6e33.jpg) ![](image/d41d8cd98f00b204e9800998ecf8427e.jpg) 参与者: 1. Computer,客户端,选择是否添加USB设备。 1. USBDevice,所有USB设备的抽象类,提供USB设备的基本通信接口。 1. USBMouse,单个对象,没有子部件。 1. USBHub,也即Composite组合对象,可能存在多个子部件。 适用性:         无论是单个对象还是组合对象,用户都希望使用统一接口来控制,这种情况下适合组合模式。 概述:        组合模式给人的感觉很像树干树枝的结构,再一想,其实和USB拓扑结构图也一样(见下图)。主机,可以看作是Client,Hub1可以看作是组合对象,包括两个USB设备以及一个新的组合设备Hub2.![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6b465f1d02.jpg) ![](image/d41d8cd98f00b204e9800998ecf8427e.jpg) ![](image/d41d8cd98f00b204e9800998ecf8427e.jpg)       无论是USB设备还是USBHub,主机都可以通过相同的命令来访问它们。这也正是组合模式要完成的工作。即Client能够通过相同的接口来访问单个设备和组合设备。 示例代码: ~~~ #include #include using namespace std; // 抽象接口 class CUSBDevice { public: virtual ~CUSBDevice(){} virtual void Add(CUSBDevice* _pDev){} virtual void Remove(CUSBDevice* _pDev){} virtual void Transmit(){} virtual int GetChild(){return 0;} }; // 单一个对象,不能添加新部件 class CUSBMouse : public CUSBDevice { public: virtual void Transmit() { cout<<"传递鼠标移动点击信息"< m_listDev; }; ~~~ // 电脑主机先接了一个HubA,HubA再接了一个USB鼠标以及又一个USBHubB // USBHubB又接一个USB鼠标 ~~~ int _tmain(int argc, _TCHAR* argv[]) { // 先建立一个USBHub CUSBHub hubA; CUSBDevice* pUSBMA = new CUSBMouse; hubA.Add(pUSBMA); // USBHubB CUSBDevice* pUSBHubB = new CUSBHub; CUSBDevice* pUSBMB = new CUSBMouse; pUSBHubB->Add(pUSBMB); hubA.Add(pUSBHubB); // 移除设备 if (pUSBMA->GetChild() > 0) { cout<<"移除设备"< ';

            8——结构型模式之桥接模式

            最后更新于:2022-04-01 20:10:43

            **定义**:桥接模式(BridgePattern),将抽象部分与它的实现部分分离,使它们都可以独立地变化。 类型:结构型模式。 类图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6abd386541.jpg) 参与角色: 1. Clinet,客户指定组装电脑,指定了最初的接口。 1. Computer,电脑抽象类,指定了具体安装CPU的接口。 1. DesktopComputer,具体的台式机电脑,根据主板型号具体实现如何安装CPU。 1. CPU,抽象类,指定连接CPU针脚的接口。 1. Pentium,Core,具体的CPU,具体实现如何连续CPU的针脚。 概述:        CPU每年都在更新,尤其是像Pentium和Core这样两代产品架构的更新,直接导致整个电脑产业链的更新。例如,Pentium和Core架构下的CPU针脚数不一样,这就要求匹配的主板也必须与CPU吻合。另外核心显卡也集成到了CPU当中云,这些都会影响到电脑的组装。        为了保证达到开闭原则,尽量不去修改原有代码,那么针对产品的升级,就必须通过继承来实现。但是电脑产品的升级会涉及到很多部件。如CPU升级了,就要派生一个新的类了。如果主板升级了,也要派生一个新的类。如果声卡升级了,也需要派生一个新的类。或者其中两个部件升级了,也需要派生新的电脑类。随着变化的部件越来越多,可能会产生非常多非常多的派生类,不便于管理。这个时候就需要用到桥接模式。       桥接模式,将每一个维度的变化都独立出来,并且做成抽象类,通过派生来实现单个维度的升级改变。这样,在客户看来,具体的产品组装接口是没有变,不影响客户端的使用。       这里的示例,主要是以CPU以及安装CPU这两个维度作一下模拟。 代码示例: ~~~ #include "stdafx.h" #include  using namespace std; class CCPU { public:    virtual ~CCPU(){}    virtual void Connect() = 0; }; class CComputer { public:    CComputer(CCPU* _pCPU) : m_pCPU(_pCPU){}    virtual ~CComputer(){}    virtual void ConnectCPU() = 0; protected:    CCPU* m_pCPU; }; class CDesttopComputer : public CComputer { public:    CDesttopComputer(CCPU* _pCPU) : CComputer(_pCPU){}    virtual void ConnectCPU()    {        cout<<"正在安装CPU"<Connect();    } }; class CPentiun : public CCPU { public:    virtual void Connect()    {        cout<<"正在安装奔腾CPU"<ConnectCPU();    delete pCPU;    delete pComputer;    // 组装酷睿台式机    pCPU = new CCore();    pComputer = new CDesttopComputer(pCPU);    pComputer->ConnectCPU();    delete pCPU;    delete pComputer;    return 0; } ~~~ **注意:**抽象类的析构函数一定要设定为虚函数,否则在内存释放的时候可能会有问题.  优缺点: 1. 优点,将实现与抽象分离,具有更好的可扩展性。并且通过组合的方式可以大大提高代码的灵活性。 1. 缺点,前期较好的识别出可变的维度有一定的困难。 参考资料: 1. 《设计模式——可复用面向对象软件基础》 1. 《Java与模式》 1. 《大话设计模式》
            ';

            7——结构型模式之适配器模式

            最后更新于:2022-04-01 20:10:41

            **定义:**适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 **类型:**结构型模式。 **适用性:** 1. 你想使用一个已经存在的类,而它的接口不符合要求。 1. 你想创建一个可以复用的类,该类可以与其他接口不兼容的类协同工作。 1. 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。可以使用对象适配器来适配它的父类接口。 **类图:** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6abd362ec9.jpg) **参与角色:** 1. Client,与Target对象类同工作的客户端. 1. Target,提供满足Client要求的接口. 1. Adapter,Target的派生类,通过调用Adpatee提供的接口来实现目标接口。 1. Adaptee,已经存在的类,需要适配,也即源接口类。 **概述:**       在GoF的《Design Pattern》中提出两种适配器模式,类适配器和对象适配器。类适配器是通过多重继承,来实现目标接口的。而对象适配器是通过组合来实现的。应组合/聚合复用原则,所以尽量不使用继承来实现适配器模式。       一说起适配器,大家马上联想到的多半会是笔记本电脑的电源适配器了。这个电源适配器也确实和上文提到的适配器模式的思想是一致的。所以这里举的示例也就使用电源适配器了。       这里以ThinkPad的某款笔记本电源适配器为例。输入电压是AC100-240V,输出电压是DC19V。例如,中国的电网电压是AC220V,但是如果出差到美国那么当地的电网电压是AC110V。像很多没有电源适配器的电器是不能在两个国家都可以使用的。但是像笔记本电脑却是可以的。这正是笔记本电脑电源适配器的作用,无论输入的是AC220V还是AC110V,经过电源适配器之后都可以转换成笔记本电脑所需要的DC19V。下面便以代码来简单描述一下电源适配器所起的作用。(AC,交流电;DC,直流电) **代码:** // 和客户进行交互的接口,即目标接口,必须提供19V电压 // 需要注意的是这里的析构函数是虚函数,这是因为保证释放子类申请的内存 ~~~ class CTarget { public: virtual ~CTarget(){} virtual int SupportComputerVoltage() = 0; }; ~~~ // 电网,提供电压接口 // 需要注意的是这里的析构函数是虚函数,这是因为保证释放子类申请的内存 ~~~ class CGrid { public: virtual ~CGrid(){} virtual int SupportVoltage() = 0; }; ~~~ // 美国电网,提供美国电压110V,也即源接口 ~~~ class CUSAGrid : public CGrid { public: virtual int SupportVoltage() { return 110; } }; ~~~ // 中国电网,提供中国电压220V,也是源接口 ~~~ class CPRCGrid : public CGrid { public: virtual int SupportVoltage() { return 220; } }; ~~~ // 适配器,转换电压100-240V电压到19V ~~~ class CAdapater : public CTarget { public: CAdapater(CGrid* _pGrid) : m_pGrid(_pGrid){} // 实现接口,提供满足电源所需要的电压 int SupportComputerVoltage() { if (NULL != m_pGrid) { cout<<"转换电压"<SupportVoltage()<<"V至19V"<SupportComputerVoltage(); if (19 != nVoltage) { cout<<"提供的电压有误,不能正常使用"<SupportComputerVoltage(); if (19 != nVoltage) { cout<<"提供的电压有误,不能正常使用"< ';

            6——创建型模式之原型模式

            最后更新于:2022-04-01 20:10:39

            **定义:**原型模式(Prototype Pattern),用原型模式指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 **类型:**创建型模式。 **类图:** ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-19_57b6abd340b2b.jpg) **参与角色**: 1. CPrototype,抽象原型基类,提供一个Clone的接口以及一些设置显示的接口。 1. CConcretePrototype,声明定义原型的相关参数,并实现Clone接口,主要是通过实现拷贝构造函数来完成的。 另外实现其他接口。 1. Client,首先定义一个原型对象,然后以此为原型克隆新的对象。 **概述:**        如果只是为了更快的完成新对象的生成,其实就没必要使用原型模式了。因为可以直接使用拷贝构造函数即可。而使用原型模式最重要的原因是隔离Client去了解具体的实现类,降低模块间的耦合。Client只需要知道一个抽象类的指针,不仅可以操作具体各咱方法,另外也还可以生成新的对象。        另外,使用拷贝构造函数时,需要注意深拷贝及浅拷贝。浅拷贝,即按位进行拷贝,即两个对象的每一个成员变量是相同的。深拷贝,自定义拷贝,一般会处理指针,引用等类型,保证它们有相同的值,而不是它们本身相同。        有一批Thinkpad电脑,除了内存和硬盘容量外,其他配置都相同。我们可以通过原型对象生成新的对象,并且不需要知道具体的实现类名。 **代码:** // 提供接口 ~~~ class CPrototype { public: virtual CPrototype* Clone() = 0; virtual void Show() = 0; virtual void SetRam(int _nRam) = 0; virtual void SetRom(int _nRom) = 0; }; ~~~ // 电脑的基本配置信息类 ~~~ class CComputerConfig : public CPrototype { public: CComputerConfig(char* _szName, int _nRomSize, int _nRamSize) : m_nRomSize(_nRomSize), m_nRamSize(_nRamSize) { if (NULL != _szName) { size_t nSize = strlen(_szName) + 1; m_szComputerName = new char[nSize]; strcpy_s(m_szComputerName, nSize, _szName); } else { m_szComputerName = NULL; } } ~CComputerConfig() { if (NULL != m_szComputerName) { delete m_szComputerName; } } CComputerConfig(const CComputerConfig& _other) { if (NULL == _other.m_szComputerName) { m_szComputerName = NULL; } else { size_t nSize = strlen(_other.m_szComputerName) + 1; m_szComputerName = new char[nSize]; strcpy_s(m_szComputerName, nSize, _other.m_szComputerName); m_nRomSize = _other.m_nRomSize; m_nRamSize = _other.m_nRamSize; } } virtual CPrototype* Clone() { return new CComputerConfig(*this); } virtual void Show() { cout<Show(); // ThindPad系列的2G内存版,500G硬盘版 CPrototype* p2GComputer = computer.Clone(); p2GComputer->SetRam(4); p2GComputer->Show(); // 4G内存,1T硬盘版 CPrototype* p1TCompter = computer.Clone(); p1TCompter->SetRam(8); p1TCompter->SetRom(1024); p1TCompter->Show(); return 0; } ~~~ **使用场合:** 1. 两个模块A,B,模块B对外暴露一个对象C指针。而模块A需要建立很多很多C的对象,但是不需要知道具体的C是如何创建的。这个时候就应该使用原型模式。 优缺点: 1. 优点,能够降低模块间的耦合性,另外能够快速的创建新对象。 缺点,改造一个已有类型时,需要细心考虑所有成员变量拷贝的问题,容易遗漏造成错误。 **参考资料:** 1. 《设计模式——可复用面向对象软件基础》 1. 《Java与模式》 1. 《大话设计模式》
            ';