设计模式(一)—— 策略模式

由于之前看的容易忘记,因此特记录下来,以便学习总结与更好理解,该系列博文也是第一次记录,所有有好多不完善之处请见谅与留言指出,如果有幸大家看到该博文,希望报以参考目的看浏览,如有错误之处,谢谢大家指出与留言。

思路:

1、模拟鸭子项目

2、项目的新需求

3、用OO原则解决新需求的不足

4、用策略模式来新需求解决

5、重新设计模拟鸭子项目

6、总结策略模式定义

一、模拟鸭子项目

1、从项目"模拟鸭子游戏"开始
2、从OO的角度设计这个项目,鸭子超类,扩展超类:
public abstract class Duck {	
public void Quack() {	
    System.out.println("~~gaga~~");
}
public abstract void display();
public void swim() {	
    System.out.println("~~im swim~~");
}
}1、GreenHeadDuck继承Duck :
public class GreenHeadDuck extends Duck {	
@Override	
public void display() {	
      System.out.println("**GreenHead**");
}
}
2、同理可有RedHeadDuck等	

二、项目的新需求

1、应对新的需求,看看这个设计的可扩展性
1)添加会飞的鸭子
2、OO思维里的继承方式解决方案是:
public abstract class Duck {
	 	...;
	 	public void Fly() {	
	 	System.out.println("~~im fly~~");
}
};

问题来了,这个Fly让所有子类都会飞了,这是不科学的。继承的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。影响会有溢出效应

三、用OO原则解决新需求的不足

1、继续尝试用OO原理来解决,覆盖:
public class GreenHeadDuck extends Duck {
...;
public void Fly() {
System.out.println("~~no fly~~");//把会飞的工程覆盖掉,实现不让他飞。}
}
这个导致,后面几十个鸭子不没有这个功能,不会飞,那么他们的都要去实现。工作量大,而且重复劳动。有人说:会飞功能不放在超类中,放在抽象函数,让子类去实现,这样需要的就去实现,不需要的不需要实现,但是这样,代码复用性却不高,比如:有10种鸭子都会飞,而且飞的方式都一样,所以实现代码都是一样的,代码复用性降低,对某些子类方便,但对其他有些子类确有影响。
2、又有新需求,石头鸭子,填坑:
public class StoneDuck extends Duck {
.... };比如石头鸭子,既不会飞,也不会叫,那么所有代码都要去覆盖。
所以:超类挖的一个坑,每个子类都要来填,增加工作量,复杂度O(N^2)。不是好的设计方式

四、用策略模式来新需求解决

策略模式:属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得他们可以相互替换。策略模式使得算法可以在不影响客户端的情况下发生变化。

所以遇到问题首先要分析项目的变化与不变化部分,对项目中的问题都可以按此思路。

需要新的设计方式,应对项目的扩展性,降低复杂度:

1)分析项目变化不变部分,提取变化部分,然后把变化的部分抽象成接口+实现

2)鸭子哪些功能是会根据新需求变化的?叫声、飞行...

1、接口:
1)public interface FlyBehavior
{	
void fly();}
	 	
2)public interface QuackBehavior
{	
void quack();};
	 	
3)抽象接口好处:新增行为简单,行为类更好的复用,组合更方便。既有继承带来的复用好处,没
有继承带来的坏处。

五、重新设计模拟鸭子项目

1、重新设计的鸭子项目:
public abstract class Duck {	
FlyBehavior mFlyBehavior;
QuackBehavior mQuackBehavior;
public Duck() { }
public void Fly() {	
mFlyBehavior.fly();
}
public void Quack() {	
mQuackBehavior.quack();
}
public abstract void display();
}

六、总结策略模式定义

1、绿头鸭、石头鸭:

在超累的基础上,子类直接在构造函数中,去new一个行为族,在用行为族中的行为去实现。

public class GreenHeadDuck extends Duck {
public GreenHeadDuck() {
mFlyBehavior = new GoodFlyBehavior();
mQuackBehavior = new GaGaQuackBehavior();
}
@Override
public void display() {...}
}

2、策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。原则就是:分离变化部分,封装接口,基于接口编程各种功能。此模式让行为算法的变化独立于算法的使用者。

七、策略模式总结与注意点

1、分析项目中变化部分与不变部分(方法论)——》这个方法论不仅是策略模式中才可以用的,用来分析项目中变法的何不变化的,变化的就可以怎么来抽取替换。而且变化的抽离出来的行为族,行为族之间是可以来相互替换的。

2、多用组合少用继承用行为类组合而不是行为的继承。更有弹性

3、如果找不到适用的模式怎么办?

(1).可能对项目分析的不够透彻。

(2).存在特殊的项目无法用设计模式,那么就只能用原始的面向对象的特性(其实设计模式也是基于oop总结提炼出来的精华所在)

八、策略模式中的设计原则

1、开闭原则(Open-Closed Principle,缩写为OCP)

      1)一个软件实体应当对扩展开放(例如对抽象层的扩展),对修改关闭(例如对抽象层的修改)。即在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展

      2)开闭原则的关键,在于抽象。策略模式,是开闭原则的一个极好的应用范例。

2、里氏替换原则(Liskov Substitution Principle,缩写为LSP)

     1)里氏替换原则里一个软件实体如果使用的是一个基类的话,那么一定适用于其子类而且它根本不能察觉到基类对象和子类对象的区别。比如,假设有两个类,一个是Base类,一个是Derived类,并且Derived类是Base类的子类。那么一个方法如果可以接受一个基类对象b的话:method1(Base b),那么它必然可以接受一个子类对象d,也即可以有method1(d)。反之,则不一定成立

     2)里氏替换原则讲的是基类与子类的关系只有当这种关系存在时,里氏替换关系才存在,反之则不存在。

     3)策略模式之所以可行的基础便是里氏替换原则:策略模式要求所有的策略对象都是可以互换的,因此它们都必须是一个抽象策略角色的子类。在客户端则仅知道抽象策略角色类型,虽然变量的真实类型可以是任何一个具体策略角色的实例

九、补充小结

策略模式的重心

策略模式的重心不是如何实现算法而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。而对于在什么情况下使用什么算法,是由客户端决定的。

算法的平等性

策略模式一个很大的特点就是各个策略算法的平等性。对于一系列具体的策略算法,大家的地位是完全一样的,正因为这个平等性,才能实现算法之间可以相互替换。所有的策略算法在实现上也是相互独立的,相互之间是没有依赖的。

所以可以这样描述这一系列策略算法:策略算法是相同行为的不同实现

运行时策略的唯一性

运行期间,策略模式在每一个时刻只能使用一个具体的策略实现对象,虽然可以动态地在不同的策略实现中切换,但是同时只能使用一个。

公有的行为

经常见到的是,所有的具体策略类都有一些公有的行为。这时候,就应当把这些公有的行为放到共同的抽象策略角色QuackBehavior类(该类只是举例这个层面的类)里面。当然这时候抽象策略角色必须要用Java抽象类实现,而不能使用接口。

十、策略模式的缺点

1)客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道算法或行为的情况

2)由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观

猜你喜欢

转载自blog.csdn.net/gududedabai/article/details/81021190
今日推荐