最近在看HeadFirst设计模式一书,做为一个半路出家的程序员,感受不少东西须要学习,学习的路程中有些东西学了当时以为理解了,但平常工做中没有使用到渐渐的本身就忘记了。----------------------上面就是写者系列的博客的缘由,主要是为了巩固知识,忘记在那个博主那边看过这么一句话,知识学了后总结了才变成本身的。程序员
策略者模式----定义了算法族,分别封装起来,让它们之间能够互相替换,此模式让算法的变化独立于使用算法的客户。算法
固然这只是理论的东西,说实话我如今都没理解这个理论是啥,下面我用代码和比较直观的语言给你们介绍一下策略者模式,但愿能有所收获。编程
模拟情节:设计模式
Joe上班的公司有一套鸭子游戏的系统:SimUDuck,游戏中有各类鸭子,一边游泳,一边呱呱叫。此系统使用了标准的OO(面向对象)技术。设计一个鸭子的超类(也就是父类-Duck),并让各类鸭子继承此超类。ide
此时因为需求的改变须要增长飞行的功能,Joe直接在超类中添加fly()方法:学习
1 public abstract class Duck 2 { 3 public void quack() 4 { 5 Console.WriteLine("我会叫"); 6 } 7 8 public void swim() 9 { 10 Console.WriteLine("我会游泳"); 11 } 12 13 public void fly() 14 { 15 Console.WriteLine("我会飞"); 16 } 17 public abstract void display(); 18 } 19 20 public class MallardDuck : Duck 21 { 22 public override void display() 23 { 24 Console.WriteLine("我是一只绿头鸭"); 25 } 26 27 } 28 public class ModelDuck : Duck 29 { 30 public override void display() 31 { 32 Console.WriteLine("我是一只模型鸭子"); 33 } 34 } 35 public class RedheadDuck : Duck 36 { 37 38 public override void display() 39 { 40 Console.WriteLine("我是一只红头鸭"); 41 } 42 } 43 44 class Program 45 { 46 static void Main(string[] args) 47 { 48 Duck modelDuck=new ModelDuck(); 49 modelDuck.quack(); 50 modelDuck.display(); 51 modelDuck.fly(); 52 Console.ReadLine(); 53 } 54 }
此时问题就来了,模型鸭子(ModelDuck)因为也是继承于超类也具有了飞行的功能,显然继承在这里并非一个很好的解决方案。这里在超类中用到了抽象的方法,有个小技巧对于新学者来讲常常搞不清抽象方法的语法,记住抽象方法是没有身体的,也就是说他是没有方法体的。spa
这时候就不能用继承了,这时候有人提出把fly()和quack()方法提到接口IFlyable与IQuackable中,这样能够飞行的鸭子实现接口,不能够飞行的模型鸭子就不实现接口,表面上看来是知足了要求,但这样的话不只仅fly()在每一个能够飞行的鸭子类中都要实现一边形成代码重复太多,若是要将能够飞行的鸭子的飞行行为稍微改动一下的话,那么面临的将是灾难,没给方法你都须要改动一下。设计
此时,你是否指望一种设计模式能来救你于苦海,这里我先卖一个关子,接下来我会用老方法找出一个解决之道,“采用良好的OO软件设计原则”;code
经过上面的例子的一步步学习,我想咱们应该知道继承并不能很好的解决问题,由于鸭子的行为在子类中是不断地变化,因此让全部的子类都有这些行为是不恰当的。接口IFlyable与IQuackable中一开始看起来还挺不错,解决了问题可是因为接口不具有实现代码,因此实现接口没法达到代码的复用。这意味者你不管什么时候要修改某个行为,都会形成你须要修改全部实现该接口的类中修改他们,这样很容易形成错误。有个设计原则能很好的解决此问题,orm
设计原则:找出应用中可能须要变化的部分,把他们独立出来,不要和那些不须要变化的代码混合在一块儿。
这是咱们介绍的第一个设计原则,在后面的学习中我还会介绍被的设计原则,咱们先对着上面的例子看看那部分是改变的,都的鸭子的行为是不断的变化的咱们须要把行为单独提出来,咱们知道Duck类内的fly()和quack()会随着鸭子的改变而不断的改变,为了把这两个行为从Duck类中分开,咱们将把他们从Duck类中取出来,创建一组新类来表明每一个行为。
设计鸭子的行为
如何设计那组实现飞行和呱呱叫的行为呢?
首先咱们但愿一切具备弹性,一开始的鸭子系统正是由于没有弹性,才让咱们走上这条路,咱们还行可以‘指定’行为到鸭子的实例。咱们想要产生一个新的绿鸭子的实例的时候,并指定特定‘类型’的飞行行为给它,干脆顺便让鸭子的行为能够动态的改变好了。换句话说,咱们应该在鸭子类中包含设定行为的方法,这样就能够在‘运行时’动态地‘改变’绿头鸭子的行为。
有了这些目标的需求,接着咱们来看咱们的第二给设计原则
设计原则:针对接口编程,而不是针对实现编程
从如今开始,鸭子的行为将被放在分开的类中,此类专门提供某行为接口的实现。这样的话和以前的作法有了明显的不一样,以前的作法是:行为来自Duck超类的具体实现,或者继承某给接口并由子类自行实现行为。这两种作法都是依赖于‘实现’形成咱们的系统再也不有弹性,很难更改别的代码。
在咱们新的实现中,鸭子的子类将使用接口(IFlyBehavior与IQuackBehavior)所表示的行为,也就是实现此接口的行为类(FlyNoWay等)具体的行为的实现如今编写在行为类中。这样的设计就使得咱们的系统具有的弹性。
这样的设计,可让飞行和呱呱叫的动做被其余的对象复用,由于这些行为已经与鸭子类无关了。而咱们也能够新增一些行为,不会影响到既有的行为类,也不会影响‘使用’到飞行行为的鸭子类。
整合鸭子的行为
关键在于,鸭子如今会将飞行和呱呱叫的动做‘委托’(delegate)别人处理,而不是使用定义在Duck类(或子类)内的呱呱叫和飞行方法。
作法的这样的:
①首先,在Duck类中‘加入两个实例变量’,分别为”iFlyBehavior“与”IQuackBehavior“,声明为接口类型(而不是具体实现接口的类的类型)每一个鸭子对象经过动态的实现这些变量以在运行时引用正确的行为类型。
咱们用两个类似的方法performFly和performQuack来取代Duck类中fly()与quack()。稍后你就知道缘由了。
②如今,咱们来实现
performQuack();public class Duck
{ IQuackBehavior iQuackBehavior;//每只鸭子都会引用实现IQuackbehavior接口的对象。 public void performQuack() { iQuackBehavior.quack();//鸭子对象不亲自处理呱呱叫行为,而是委托给iQuackBehavior引用的对象。
}
}
Duck对象只要叫iQuackBehavior对象去呱呱叫就能够了,在这部分的代码中,咱们不在关心iQuackBehavior接口的对象究竟是什么,咱们只关心该对象知道如何进行呱呱叫就够了。
3.如今关心如何实现iQuackBehavior与iFlyBehavior的实例变量,看看具体继承Duck类的子类是怎么实现的吧
public class ModelDuck : Duck { public ModelDuck() { iFlyBehaviro = new DuckNoFly(); iQueackBehaviro = new DuckNoQueck(); } public override void Display() { Console.WriteLine("我是一只木头鸭子"); } }
在ModelDuck实例化时,它的构造器会把继承来的iFlyBehavior和iQuackBehavior实例变量初始化成DuckNoFly与DuckNoQuack类型的新实例。(DuckNoFly与DuckNoQuack是接口的具体实现)
如今只剩下一个在运行时动态该百年鸭子的行为的功能还未实现,咱们上面是在具体鸭子类的构造器内进行更改鸭子的行为的,若是换一个地方将更改鸭子的行为提取到Duck超类中,那么只要继承自该超类的鸭子派生类不都具有了动态更改行为的能力了吗?
在Duck类中,加入两个新方法:
public void setPerformFly(IFlyBehaviro fb) { iFlyBehaviro = fb; } public void setPerformQuack(IQuackBehaviro qb) { iQueackBehaviro = qb; }
今后之后,咱们能够‘随时’调用者两个方法改变鸭子的行为。下面我贴上更改后的所有代码:
public abstract class Duck { public IFlyBehaviro iFlyBehaviro; public IQuackBehaviro iQueackBehaviro; public abstract void Display(); public void Swim() { } public void performFly() { iFlyBehaviro.fly(); } public void performQuack() { iQueackBehaviro.quack(); } public void setPerformFly(IFlyBehaviro fb) { iFlyBehaviro = fb; } public void setPerformQuack(IQuackBehaviro qb) { iQueackBehaviro = qb; } } public class ModelDuck : Duck { public ModelDuck() { iFlyBehaviro = new DuckNoFly(); iQueackBehaviro = new DuckNoQueck(); } public override void Display() { Console.WriteLine("我是一只木头鸭子"); } } public interface IFlyBehaviro { void fly(); } public interface IQuackBehaviro { void quack(); } public class DuckNoFly : IFlyBehaviro { public void fly() { Console.WriteLine("我不会飞"); } } public class DuckCanFlay:IFlyBehaviro { public void fly() { Console.WriteLine("我会飞"); } } public class DuckNoQueck : IQuackBehaviro { public void quack() { Console.WriteLine("我不会叫"); } } public class DuckGuaGuaQueck:IQuackBehaviro { public void quack() { Console.WriteLine("呱呱叫"); } } class Program { static void Main(string[] args) { Duck modelDuck = new ModelDuck(); modelDuck.Display(); modelDuck.performFly(); modelDuck.performQuack(); modelDuck.setPerformFly(new DuckCanFlay()); modelDuck.performFly(); Console.ReadLine(); } }
封装行为的大局观
咱们已经深刻研究了鸭子模拟器的设计,该是看看总体的格局了:
这是从新设计后的类结构,你所指望的一切都有:鸭子继承Duck,飞行行为实现IFlyBehavior接口,呱呱叫行为实现IQuackBehavior接口。请特别注意类之间的”关系“,关系能够是IS-A(是一个)、HAS-A(有一个)或IMPLEMENTS(实现)。
”有一个“可能比”是一个“更好
”有一个“关系至关有趣:每个鸭子都有一个IFlyBehavior和一个IQuackBehavior,好将飞行和呱呱叫的委托给它们代为处理。
当你将两个类结合起来使用,如同本例通常,这就是组合。这种作法和”继承“不一样的地方在于,鸭子的行为不是经过继承来的,而是和适当的行为对象”组合“来的。
这是一个很重要的技巧,其实也是咱们介绍的第三给设计原则:
设计原则:多用组合,少用继承。
如你所见,使用组合创建系统具备很大的弹性,不只能够将算法组封装成类,更能够”在运行时动态的改变行为“,只要组合的行为对象符合正确的接口标准便可。”组合“被普遍的应用在不少设计模式中,后面你会常常发现它的身影。
至此,这就是咱们学习的第一个模式了。
在上面的内容中咱们须要到了几项内容:
OO基础:抽象,封装,多态,继承。
OO原则:封装变化;多用组合,少用继承;针对接口编程,不针对实现编程。
OO模式:策略模式----定义算法族,分别封装起来,让他们直接能够互相替换,此模式让算法的变化独立于使用算法的客户。
初衷,只是想把学习的设计模式本身总结成博客记录下来,可是好像有点偏离初心更像是一个教别人怎么理解策略者模式了,水平有限,写的不是很好,对本内容感兴趣的读者,推荐你阅读《Head First设计模式》一书,该书很生动的讲解了设计模式。我上面所写的内容也是基于此书。最后感谢您的阅读。