Strategy Pattern - 策略模式
一、概念
Strategy Pattern - 策略模式,定义了一些列的算法,并将每个系列封装起来,这使得它们可以互换。策略让我们可以独立于使用它的客户端来改变算法。
二、从示例中学习
假设我们在某厂台灯厂工作,该厂生产各式各样的台灯。现在厂里要求做个应用程序向用户展示各类台灯的工作方式。所以我们又不能偷懒了:(,工作开始……
首先我们分析了下,每个台灯(Lamp)都有各自的样式(Display);一个控制按钮(Pushbutton),按下按钮灯便开始发光。所以我们设计了个Lamp基类,它有两个方法display()与push()。
{
//每种台灯都有按钮
public void Push()
{
Console.WriteLine("I'm shining now!");
}
//样式因lamp而异,所以Display()设置方法抽象
public abstract void Dsplay();
}
接着A灯、B灯、...都继承了基类Lamp,同时各自改写Display(),这样它们便拥有了与众不同的样式。
class Alamp : Lamp
{
public override void Display()
{
Console.WriteLine("Alamp-style is shown");
}
}
class Blamp : Lamp
{
public override void Display()
{
Console.WriteLine("Blamp-style is shown");
}
}
//
大功告成!
可是一年后,我们厂意识到在台灯下工作的人都是多么的重视时间,所以将所生产的所有台灯都装上了一个小时钟。为了节约成本,部门领导要求我们不用重写展示程序了,只要相应的添加个时钟(Timer)就可以了。嘿嘿,多么轻松的工作,我们只要在基类Lamp里加个Timer()方法,这样所有的lamp因为继承了Lamp便都有了个Timer()。
{
Console.WriteLine("Show time.");
}
可是不幸的是,厂子的这个决策导致上半年的业绩狂跌。原因是,并不是所有人都喜欢有个时钟在台灯上!于是领导层又发出指示:有时钟与无时钟品种按3/7生产。那么我们当然又要——
那么接下来我们是否想到,既然Display因灯而异,我们把它设置为抽象方法,然后再实现中重写,那么我们的Timer不是也可以这样,辛好灯的类型不多,我们在全部的lamp中加个Timer的重写代码?!继续思考,要是我们有些灯又加了个日历表呢?如果采用Timer这样的处理方法,系统便没有了代码重用这回事了,不是吗?而且重写的工作劳苦啊!有加班费就无所谓了:)
于是我们想到了采用接口,将可有可无的功能设置为接口,如Timer、Calendar;而将一定有的特性放在基类中,如Display。代码如下:
将变化的部分分离并封装起来,那么这块代码便不会影响其它的代码了,这样可以减少改变代码时出现的意外结果,同时也使我们的系统更加灵活。
那么我们回过头再思考下采用接口后的程序。由于Timer和Calendar都是变化的,我们把它们重基类Lamp中分离出来了。我们还可以做哪些工作呢?
我们可以定义两个接口类:
//ITimerBehavior接口
public interface ITimerBehavior
{
void Showtime();
}
//ICalendarBehavior接口
public interface ICalendarBehavior
{
void Showdate();
}接着,我们将具体的行为放入实现了特定行为接口的类中,这样各种lamp类就不用知道他们自己到底有什么behaviors(时钟或日历):
//将具体的行为放入实现了特定行为接口的类中
class TimerExist : ITimerBehavior
{
void ShowTime()
{
Console.WriteLine("With a timer.");
}
}
class TimerNonexist : ITimerBehavior
{
void ShowTime()
{
Console.WriteLine("Without a timer.");
}
}
class CalendarExist : ICalendarBehavior
{
void ShowDate()
{
Console.WriteLine("With a Calendar.");
}
}
class CalendarNonexist : ICalendarBehavior
{
void ShowDate()
{
Console.WriteLine("With a Calendar.");
}
}先举个例子(来自书中)。比如有个抽象类Animal,带有两个具体的实现,Dog和Cat。
//Programming to an implementation
Dog d = new Dog();
d.bark();
//Programming to an interface.supertype
Animal animal = new Dog();
animal.makeSound();
//assign the concrete implementation object at runtime
a = getAnimal();
a.makeSound();如上所示,我们如果编写接口的话可以让我们多态的引用animal,甚至可以在运行时分配给animal一个具体的实现。即使在不知道a是什么动物的情况下,我们也知道如何响应makeSound()。
回到例子前面的代码,这样的设计使得其它的对象也可以使用ShowTime和ShowDate行为,因为这些行为不再被lamp类独占。这样也就得到了Reuse的好处。而且我们可以增加各种behavior而不改变任何已存在的behavior,或影响到使用ShowTme或ShowDate的lamp类。
集成Lamp的Behavior
接下来我要集成Lamp的Behavior,关键点是现在我们的Lamp基类将委派ShowTime和ShowDate行为。
- 首先添加两个引用变量:
-
//声明两个行为接口类型的变量,注意要设置为public,因为所有的子类需要继承这些
public ITimerBehavior timerBehavior;
public ICalendarBehavior calendarBehavior;
-
- 实现performShowTime()方法:
-
public void performShowTime()
{
//委派(delegate)行为类
timerBehavior.ShowTime();
}
public void performShowDate()
{
calendarBehavior.ShowDate();
}
-
- 实现要生产的台灯类:
-
public class Alamp : Lamp
{
public Alamp()
{
//带有时钟与台历
timerBehavior = new TimerExist();
calendarBehavior = new CalendarExist();
}
public override void Display()
{
Console.WriteLine("Alamp-style is shown.");
}
}
public class Blamp : Lamp
{
public Blamp()
{
//无时钟,有台历
timerBehavior = new TimerNonexist();
calendarBehavior = new CalendarExist();
}
public override void Display()
{
Console.WriteLine("Blamp-style is shown.");
}
}
public class Clamp : Lamp
{
public Clamp()
{
//有时钟,无台历
timerBehavior = new TimerExist();
calendarBehavior = new CalendarNonexist();
}
public override void Display()
{
Console.WriteLine("Clamp-style is shown.");
}
}
-
- 有客户购买了台灯并使用:
-
//测试
class Program
{
static void Main(string[] args)
{
Lamp alamp = new Alamp();
alamp.Display();
alamp.Push();
alamp.performShowTime();
alamp.performShowDate();
Console.WriteLine("/****************/");
Lamp blamp = new Blamp();
blamp.Display();
blamp.Push();
blamp.performShowTime();
blamp.performShowDate();
Console.WriteLine("/****************/");
Lamp clamp = new Clamp();
clamp.Display();
clamp.Push();
clamp.performShowTime();
clamp.performShowDate();
Console.ReadLine();
}
}
-
- 完整代码:Design_Pattern(.rar)
三、面向对象设计
- OO要素:
- 抽象
- 封装
- 多态
- 继承
- OO原则:
- 封装变化点
- 优先使用对象组合,而不是类继承
- 针对接口编程,而不是针对实现编程
四、更多资源
- MSDN WebCast 李建忠老师的:C#面向对象设计模式纵横谈
- 吕震宇老师的: 设计模式(22)-Strategy Pattern
|
本篇内容仅为个人学习笔记,多数内容参考自书中。仅作参考。 |
|
本Blog中所有内容皆以“现状”提供且没有任何担保,同时也没有授予任何权利。 |
浙公网安备 33010602011771号