1、 模式一:策略模式
1.1策略模式定义:
策略模式定义了算法簇,分别封装起来,让他们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
1.2应用场景
现任举一现实案例如下,以此来模拟软件开发中的问题事实。
设Human类如下:public class Human
{
public void Eat()
{
Console.Write(“吃饭”);
};
public void Drink();
{
Console.Write(“喝水”);
}
}
现实世界中任何一个具体的人都继承于该Human类。现假设出现了会游泳的人,一种常见的做法是在父类中加入Swin()方法。很明显,这么做会因为继承带来问题,因为并不是所有人都会游泳。而且即使所有人都会游泳,每个人游泳的方式也不尽相同。当然,这里每个人游泳方式的不同可以用多态(方式可采用虚方法或接口实现)来解决,但如果A,B2人恰巧都采用同一种游泳方式,则多态无法解决AB之间代码重用问题。那么,如何用一种方法同时解决继承和多态所带来的问题,这个时候,我们可以用策略模式来解决。
1.3策略模式分析与实现(C#描述)
对上述问题进行分析,我们发现:Swin()是一种动作,而且是一种变化的动作。根据策略模式定义,策略模式是对一系列算法的封装,算法此处恰好意味着动作,即对一系列动作的封装。每个人游泳的动作可能不同,这里的算法簇集合就是游泳动作的集合。另外,OO原则告诉我们,应该尽量找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混合在一起。对应到该例:Swin()不像Eat()和Drink()(每个人都一定会吃饭和喝水),是一个变化的因素,所以这里,我们将Swin个这动作从父类中抽取出来,并定义一个单独的Swin接口:
public interface ISwinBehavior
{
void Swin();
}
然后每一种具体的Swin动作作为一个单独的类实现该接口。
例如蛙泳定义为:public class FrogSwin : ISwinBehavior
{
public viod Swin()
{
Console.WriteLine("蛙泳");
}
}
对应于策略一。
蝶泳定义为:public class ButterflySwin:ISwinBehavior
{
public viod Swin()
{
Console.WriteLine("蝶泳");
}
}
对应于策略二。
这里之所以把Swin个这动作从父类中抽离出一个接口,然后具体的动作类实现该接口,而不是直接抽离成一个个具体的类。原因为根据上述OO原则,我们应该尽量针对接口编程,则不是针对具体实现,这里如果抽离成的一个个具体的类,实际上就是一个个具体的实现。那么为什么要针对接口而不是实现编程,一会再具体描述。下面继续策略模式,动作被抽离成策略并定义好实现形态后,我们改变父类的定义如下:
//客户父类
public class Human
{
public ISwinBehavior ISwin;
//以组合的方式定义接口,符合面向对象原则,多用组合,少用继承。
public void Eat()
{
Console.WriteLine("吃饭");
}
public void Drink()
{
Console.WriteLine("喝水");
}
public void Swin()
{
ISwin.Swin();
}
}
子类定义如下:
//客户子类
public class XiaoMing : Human
{
public XiaoMing()
{
this.ISwin = new FrogSwin();
}
public void ChangeSwinBehavior()
{
this.ISwin = new ButterflySwin();
}
}
调用代码如下:
//调用类
public class Strategy_Test
{
public static void Do()
{
XiaoMing lobj_XiaoMing = new XiaoMing();
lobj_XiaoMing.Swin();//输出蛙泳
lobj_XiaoMing.ChangeSwinBehavior();
lobj_XiaoMing.Swin();//输出蝶泳
}
}
至此,策略模式完毕。
通过策略模式,解决了上述继承和多态所引发的问题。首先关于继承的问题:父类中只包含游泳这一行为接口,而不是具体实现,这样子类并未依赖于父类的具体实现,这样就避免了如果在父类中写入实现子类却不会游泳的问题,策略模式中如果不会游泳,只需在子类构造函数中将接口赋以不会游泳的实现类(即不会游泳策略)即可。其次关于多态不能重用代码的问题:由于策略被抽离成不同的实现类。这样如果AB 均会蛙泳,则只需在AB的构造函数中给接口赋以相同的策略,而策略类本身实现了在不同的子类中重用。而实际上,该策略类将不只可以用于HUMAN类,而是可以用于任何有Swin形为的类,即策略可以跨类重用。
上面讲了OO原则应该尽量针对接口而不是实现编程,那么这里分析一下为什么要针对接口而不是实现编程。本人认为:接口代表的是一种不变,而具体实现代表变化,接口是就用来解决软件开发中的变化对程序带来的负面影响(可维护性差)的一种软件领域的工具。我们都知道,在软件开发领域,唯一不变的就是变化本身。举一个软件开发中的实例,在财务软件开发中,算工资这个动作是不变的,而具体根据什么公式算却是经常变化的。那么算工资这个动作可以理解成接口,而怎么算则是具体实现。在软件开发过程中,如果我要算工资,那么算工资的调用者所做的只是调用算工资这个接口,至于该接口的实现类是根据什么公式计算工资的,调用者并不需要知道,一旦工资计算方式发生变化时,我们所做的只需改变接口的实现类,而调用者(客户)是不要做任何改动的。如果现实世界算工资都是从来没有变化的,那么我们根本不需用到接口,直接调用实现类就行了。另外,通过接口还可以在运行期间动态改变类的行为方式。一如上例:我们可以在运行时态动态改变XiaoMing的游泳方式,将其本身为蝶泳的方式改为蛙泳。