因为课题需要,我必须能够读懂大佬的源代码框架,所以我必须对设计模式有所了解。
我会将我的理解记录于此,如有差错,望朋友指正。
第一个模式叫做策略模式,我认为它不应该算是一种模式,而是一种为了提高代码的复用性降低耦合度的一种思想,整个面向对象的编程中都应该充斥着这种思想,是所有设计模式的基石。
开篇例子是这样的,有个模拟鸭子游戏,叫做SimUDuck,游戏中会出现各种鸭子,一边游泳一边呱呱叫,这个系统的内部设计采用标准的OO技术,设计了一个鸭子基类,并让各种鸭子继承此基类。
abstract class Duck
{
public void Swim()
{
Console.WriteLine("I can Swim");
}
public void Quack()
{
Console.WriteLine("I can Quack");
}
public void Fly()
{
Console.WriteLine("I can fly");
}
abstract public void Display();
}
class GreenDuck : Duck
{
public override void Display()
{
Console.WriteLine("I have green head");
}
}
class RedDuck : Duck
{
public override void Display()
{
Console.WriteLine("I have red head");
}
}
///其他鸭子类似,只有外观重写
原书中采用的Java语言描述的,我采用的C#,以后看大话设计模式,就都是C#啦。
这里用了抽象方法(注意C#抽象方法与虚方法的区别,Java只有抽象方法没有虚方法)来实现整个过程,看似正常,但是非常不具有可拓展性,例如有的鸭子是玩具鸭,并不能飞,但是能叫,按照这样的设计,实例出来的玩具鸭同样会具有飞的能力,那就不符合要求了,那么怎么改?
按着上文的代码思路,我们只有修改基类,将Fly()方法同样做成抽象方法,然后对每个继承的子类进行修改,然后如果又出现一种装饰的木头鸭,不能叫也不能飞,那么怎么办呢?我同样将Quack()方法做成抽象方法,再这样一个一个再去重写子类吗?
这显然就是牵一而发动全身,所有的代码结构都需要很大的修改而且有很多重复的工作量产生,比如就是普通的会游泳会叫的鸭子,却因为做成了抽象方法,而不得不每个都重写,这样就很不方便了,想想修改我现有代码的OO结构,有没有更好的方式来实现?
接口我们可以用上嘛,既然如此,那我们可以将Fly()和Quack()方法单独拿出来做成两个接口,所有的鸭子可以选择去实现并重写接口中的方法~看上去好像很有道理,但是实质上与抽象方法没有区别,不是一样这么大工作量吗,还有没有更好的办法呢?
首先再想想我们的需求,我们想设计一种代码结构,当新的变化来临的时候,我们仅仅需要小幅改动代码就可以了,而不需要大范围改动,以上两种方式都需要大范围修改代码,很不恰当。这里就引出了我们的设计原则:
“找出应用中可能需要变化的地方,把它们独立出来,不要和那些不变的功能混在一起”
那么按照这个设计原则,我们首先想一想,每个鸭子的外观是不同的是需要实例化的,那么这就是一个不变的功能,其他的叫,飞和游泳,都有可能因为鸭子类型的不同而变化,因而我们需要将这三个方法分离出来。
正确的做法是针对接口编程,针对基类编程,而不是针对实现编程,说的更加直白一点,我们是将可能变化的地方做成类或者接口保存起来,在子类中保持对这些变化功能的引用就可以了,接下里看看如何实现的吧(仅展示了Fly功能,Quack和Swim是一样的结果)
public interface FlyBehavior//飞行动作的接口
{
void Fly();
}
public class FlyWithWings : FlyBehavior//一种飞行类
{
public void Fly()
{
Console.WriteLine("I can fly");
}
}
public class FlyNoWay : FlyBehavior//另一种飞行类
{
public void Fly()
{
Console.WriteLine("I can't fly");
}
}
abstract class Duck
{
public FlyBehavior flyBehavior;//保存对变化部分的引用(飞行行为)
public void setFlyBehavior(FlyBehavior fb)
{
//调用此方法可以动态改变鸭子的飞行行为
flyBehavior = fb;
}
abstract public void Display();
}
class GreenDuck : Duck
{
public void performFly()
{
//飞的执行方法
flyBehavior.Fly();
}
public GreenDuck()
{
//构造函数,相当于默认GreenDuck对象是会飞的
flyBehavior = new FlyWithWings();
}
public override void Display()
{
Console.WriteLine("I have green head");
}
}
///采用这样的方式,可以十分灵活的改变不同鸭子的行为,而且如果想新增鸭子的飞行方式也简单,不过
///是一个新类再实现一个FlyBehavior接口就可以了
///下面是具体实现
class Program
{
static void Main(string[] args)
{
GreenDuck greenduck = new GreenDuck();
greenduck.performFly();
greenduck.setFlyBehavior(new FlyNoWay());
greenduck.performFly();
Console.Read();
}
}
///结果输出两行,分别为I can fly 和I can't fly.
总结一下什么是策略模式:策略模式定义了一系列算法,从概念上讲,这些算法的功能相同,于是将它们封装起来,让它们之间可以相互切换,于是降低各种算法类与使用算法类之间的耦合(让算法的变化独立于使用它们的客户)。
这样一看却是很简单,但是回想起这样做的目的到底什么似乎又有点模糊,仔细想想我当初为什么要改变成这种设计策略,这种策略到底好在哪里,还需要我们在不同的场景中反复应用,才能真正有这种OOP编程思想。
一样的情景在Unity中也时常会遇到,例如一个FPSgame,一个大的角色类,其子类是各种不同的游戏角色,每个角色一次只能拿一个武器,但是可以在游戏过程中切换武器……