设计模式一
设计模式?到底什么是设计模式?还是直接去抄一下定义吧。设计模式是针对上下文的特定问题的解决方案。也就是一种解决问题的思路。
在说设计模式之前,我们还是要提一下七大原则:开闭原则、依赖倒转原则、单一职责原则、里氏替换原则、复合复用原则、迪米特原则、接口隔离原则。这几个原则就是简单的说一下,并不是需要花太多的时间去说,因为说多了也记不出。
一、状态模式
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。其实吧,当我使用这个状态模式的时候,感觉这个状态模式可以理解成为具体对象抽象化,就是我们所说的子类父类的关系,父类中的方法都是一般化,子类具体实现就行了。使用这个模式的时候我们需要定义一个入口对象(上下文环境)Context ,在这个对象中具体去实现你要实现的对象就行了。其实这样做的好处就是我们不用大量的去对相同结构的对象去写一些相同的字段或者方法了,下面是网上的图片:
这个state抽象类你可以不用写成抽象,也可以写成一般类或者接口,因为状态模式的本质就是子类一般化。在这中模式中我们一般会使用一个controller来管理状态,不同的state在这个controller中进行切换。
二、外观模式
外观模式是一种使用频率非常好的结构型设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供了一个统一的入口,降低子系统与客户端之间的耦合度,且客户端调用非常方便。
说到这里,其实我们并不难的理解外观模式是用来减少耦合度的。我们也可以这样去理解,在软考中的1:m,这样就可以很容易去知道了1是指的客户端,也就是外界访问,那就是多个子系统。这里要特别说明的一下的是,子系统之间是不可以相互调用,如果调用的话就变成了中介者模式了。外界与内部调用是通过一个中间变量来执行的。下面是图:
三、单例模式(跳过)
四、中介者模式
首先第一句中介者模式的关系是m:n。为了是思路更加明白,中介者模式只是对子系统之间而言的,并不对外界有影响。每个子系统如果想要去调用别的子系统的话,我们一般的做法不会让他们直接去调用的,而是去找个中间变量去来管理他们,这个中间变量就是中介者。说到这里我们其实已经很明白外观模式与中介者的区别了,但是我们在实际开发中,我们还是会把他们放在一起使用的,因为分外写没有必要。他们本质就是管理,使代码之间的耦合降低,功能单一。这个是非常符合软件开发的单一指责原则的。图:
五、桥接模式
关于这个模式,我们还是先来看看这个模式的意图吧。将抽象部分与它的实现部分分离,使他们可以独立的变化。在学习这个部分的时候,我们不能把它单独的来说,一定要与前面进行比较来学。状态模式是将具体类抽象成一个类,客户端与这个抽象类进行打交道,这个特点是一个实体,一个是抽象。我们现在做的一件事情就是,当我们的客户变成多个具有相同特点,又有一些不同的时候的,那我们有应该这么去做呢?最好的办法就是将客户端也进行抽象,那么就变成了两个抽象类进行交互了,这样的连接模式就是桥接模式。在这里的时候我们发现,就是在这前面的模式,他们都是为了减少耦合的,为了这个,就需要提取公共的特点,这个提取的过程就是抽象类。或者可以这样的去认为,桥接模式就是两个状态模式。图:
六、工厂模式
在面向对象编程中,最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下,new操作符直接生成对象会带来一些问题。举个例子说,许多类型对象的创建需要一系列的步骤:你可能需要计算或取得对象的初始设置;选择生成哪个子对象实例;或在生成你需要的对象之前必须先生成一个辅助功能的对象。在这些情况新对象的建立就是一个“过程”。不仅是一个操作,像一个大机器中的一个齿轮传动。
工厂模式分为简单工厂模式、工厂方法模式、抽象工厂模式。我们一个一个说明。
简单工厂模式
代码如下:
#region 产品类 //基类 一般的作用就是将子类的共有的方法汇集过来 //避免重复写 public abstract class BWM { public BWM() { } } public class BWM320 : BWM { public BWM320() { Console.WriteLine("制造-->BWM320"); } } public class BWM523 : BWM { public BWM523() { Console.WriteLine("制造-->BWM523"); } } #endregion #region 工厂类 public class Factory { public BWM CreateBWM(int type) { switch (type) { case 320: return new BWM320(); case 523: return new BWM523(); default: break; } return null; } } #endregion #region 客户类 class Program { static void Main(string[] args) { Factory factory = new Factory();//实例化简单工厂 BWM bWM320 = factory.CreateBWM(320); BWM bwm523 = factory.CreateBWM(523); Console.ReadKey(); } } #endregion
我们从上面的代码上可以看出,先将产品的基类写出来,然后再生成子类,这个部分就是产品类;接着我们的做法就是直接创建一个工厂类,这个工厂类是一个普通的类,在这个里面我们写一个方法,在外面可以通过这个方法传参,就可以在factory类中创建你需要指定的产品类了,但是这个方法有一个缺点,如果我们这个时候再来一个产品类的话,我们就是在factory再进行修改。这样做的方式是不好的,因为我们在设计软件软件中有一个原则 就是对拓展开放,对修改封闭。就是我们在进行创建的时候,我们应该知道如果多个类他们的方法是一样的,但是需要不同的数据的话,我们就应该为他们创建一个基类,所以这个factory也是应该创建一个基类的,所有下面就是第二个了。
工厂方法模式
#region 产品类 public abstract class BMW { public BMW() { } } public class BMW320 : BMW { public BMW320() { Console.WriteLine("制造-->BMW320"); } } public class BMW523 : BMW { public BMW523() { Console.WriteLine("制造-->BMW523"); } } #endregion #region 创建工厂类 interface FactoryBMW { BMW CreateBMW(); } public class FactoryBMW320 : FactoryBMW { public BMW CreateBMW() { return new BMW320(); } } public class FactoryBMW523 : FactoryBMW { public BMW CreateBMW() { return new BMW523(); } } #endregion class Program { static void Main(string[] args) { FactoryBMW320 factoryBMW320 = new FactoryBMW320(); BMW320 bMW320 = (BMW320)factoryBMW320.CreateBMW(); FactoryBMW523 factoryBMW523 = new FactoryBMW523(); BMW523 bMW523 = (BMW523)factoryBMW523.CreateBMW(); Console.ReadKey(); } }
其实我们不难发现就是将工厂进行抽象化,就是所说的那个提取基类,我们在使用的时候只需要用指定的基类。如果你是需要使用BMW320的话,你只要去实例化BMW320的工厂类就行了,但是这样的话还是出现一个问题,如果我们又有新的功能的话,又怎么办了?其实在这种功能经常进行变更的地方,我们一定要注意抽象方法提取基类的思想。
下面就是抽象工厂方法
#region 产品类 //发动机以及型号 public interface Engine { } public class EngineA : Engine { public EngineA() { Console.WriteLine("制造-->EngineA"); } } public class EngineB : Engine { public EngineB() { Console.WriteLine("制造-->EngineB"); } } //空调以及型号 public interface Aircondition { } public class AirconditionA : Aircondition { public AirconditionA() { Console.WriteLine("制造-->AirconditionA"); } } public class AirconditionB : Aircondition { public AirconditionB() { Console.WriteLine("制造-->AirconditionB"); } } #endregion #region 创建工厂类 //创建工厂的接口 public interface AbstractFactory { //制造发动机 Engine CreateEngine(); //制造空调 Aircondition CreteAirconition(); } //为宝马320系列生产配件 public class FactoryBMW320 : AbstractFactory { public Engine CreateEngine() { return new EngineA(); } public Aircondition CreteAirconition() { return new AirconditionA(); } } //为宝马523系列生产配件 public class FactoryBMW523 : AbstractFactory { public Engine CreateEngine() { return new EngineB(); } public Aircondition CreteAirconition() { return new AirconditionB(); } } #endregion class Program { static void Main(string[] args) { //生产宝马320系列配件 FactoryBMW320 factoryBMW320 = new FactoryBMW320(); factoryBMW320.CreateEngine(); factoryBMW320.CreteAirconition(); //生产宝马523系列配件你 FactoryBMW523 factoryBMW523 = new FactoryBMW523(); factoryBMW523.CreateEngine(); factoryBMW523.CreteAirconition(); Console.ReadKey(); } }
其实这样的做法就是我们给工厂的子类提供一个接口,如果我们添加需求的时候,我们就可以在工厂子类里进行显示。还是那句话,提取基类。
无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是解耦。在使用时,我们不必去在意这个模式到底是工厂模式还是抽象工厂模式,因为他们之间的演变常常是令人捉摸不透的。经常你会发现,明明使用的工厂模式,当新需求来临时,稍加修改,加入了一个新的方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式,当减少一个方法时的提供的产品不再构成产品族之后,它就演变了工厂方法模式。
七建造者模式
建造者模式使用多个简单的对象一步一步构造一个复杂的对象。这种类型的设计模式属于创建者模式,它提供了一种创建对象的对象方式。在编程中,我们还是要知道一点的,就是当你去创建一个对象的时候,如果这个对象的功能实在是太多了。比如说,这个对象需要创建身体,需要创建武器,需要创建特效等等,当我们把它的具体实现类全部放在一个类中去完成,这显然是不好的,因为这个违背了对修改关闭,对拓展开放的原则。管理者其实要做的就是我们只要知道流程就行了,具体的可以让各个部分去做,其实建造者模式就是这个原理。建造者会把一个功能庞大的对象进行分解,每一个部分只需要去完成一个小的部分,然后管理者再进行组装,如果有的地方要使用对象的话,就直接向这个管理者提要求,让管理者去做就行了。
十一命令模式
将一个请求封装成一个对象(即我们创建的command对象)从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。子软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但在某些场合,比如需要对行为进行记录、撤销或者重做、事务等处理时,这种无法抵御变化的紧耦合的设计模式就不太合适了。我们在设计软件的时候,如果让接受与发送者两个职能放在一起的话,就会出现耦合性比较高的情况,我们要做的就是将职能进行分开,让发送与接受分成单独的类
/// <summary> /// 接收者类,知道如何实施与执行一个请求相关的操作,任何类都可能作为一个接收者 /// </summary> public class Receiver { /// <summary> /// 真正的命令实现 /// </summary> public void Action() { Console.WriteLine("Excete request"); } } /// <summary> /// 抽象命令类,用来声明执行操作的接口 /// </summary> public interface ICommand { void Excete(); } /// <summary> /// 具体命令类,实现具体命令 /// </summary> public class ConcerteCommand : ICommand { private Receiver receiver; //具体命令类包含有一个接收者,将这个接收者对象绑定与一个动作 public ConcerteCommand(Receiver receiver) { this.receiver = receiver; } /// <summary> /// 说这个实现是虚的,因为他是通过调用接收者相对应的操作来实现Excete的 /// </summary> public void Excete() { receiver.Action(); } } /// <summary> /// 调度类,要求该命令执行这个请求 /// </summary> public class Invoker { private ICommand command; /// <summary> /// 设置命令 /// </summary> /// <param name="command"></param> public void SetCommand(ICommand command) { this.command = command; } /// <summary> /// 执行命令 /// </summary> public void ExceteCommand() { command.Excete(); } } class Program { static void Main(string[] args) { Receiver receiver = new Receiver(); ICommand command = new ConcerteCommand(receiver); Invoker invoker = new Invoker(); invoker.SetCommand(command); invoker.ExceteCommand(); Console.ReadKey(); } }
我们从代码中可以看出,我们先要初始化一个receiver(接收者类),这个类是你不需要知道你去让外界去工作,只需要外界发出命令请求的时候,这个类只需要去做就行了,这个是一个单向的,就比较像mvc模式中的view类一样,只需要去做就行了。另外再说一句就是这个receiver类是一个具体执行的类。