Head First 设计模式学习——策略模式
来源:互联网 发布:文渊阁四库全书数据库 编辑:程序博客网 时间:2024/05/20 13:38
设计模式是进阶高级开发的必经之路。掌握设计模式,才能提升编码能力,设计出可复用、可扩展、可维护的软件系统。了解设计模式,才能更好理解开源类库的实现原理,解决问题。
策略模式(Strategy Pattern)是《Head First 设计模式》介绍的第一个模式。本文将做介绍,并介绍此模式在JDK中的应用。
一、定义
策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,独立于使用算法的客户。
二、举例
背景:一个游戏中有各种各样的鸭子模型。有不同的外观,会游泳、呱呱叫,用java实现。
稍微有面向对象编程(OOP)经验的程序猿都会想到,应该有个鸭子超类,抽象鸭子的共同属性、行为。其他的鸭子具体实现。如
v1.0版//鸭子超类public abstract class Duck { //鸭子类型 private String duckName="Duck"; public Duck(String duckName) { this.duckName = duckName; } //不同鸭子外观不同,所以由子类实现 public abstract void display(); //所有鸭子都会呱呱叫,由父类实现 public void quack(){ System.out.println(duckName+":会呱呱叫"); } //所有鸭子都会游泳,由父类实现 public void swim(){ System.out.println(duckName+":会游泳"); }}
此时有GreenDuck:
class GreenDuck extends Duck{ public GreenDuck(String duckName) { super(duckName); } //具有不同外观的绿鸭实现display()方法 @Override public void display() { System.out.println(duckName+":是绿色的"); }}
此外,还有RedDuck
,BlueDuck
等各种鸭子。他们都继承Duck
类,实现自己的外观,在游戏里游泳、发出呱呱叫的声音,一切都很和谐,玩家也很喜欢:
public static void main(String[] args) { GreenDuck greenDuck = new GreenDuck("green duck"); greenDuck.swim(); greenDuck.quack(); greenDuck.display(); RedDuck rednDuck = new RedDuck("red duck"); rednDuck.swim(); rednDuck.quack(); rednDuck.display(); }控制台打印:green duck:会游泳green duck:会呱呱叫green duck:是绿色的red duck:会游泳red duck:会呱呱叫red duck:是红色的
唯一不变的是变化,好景不长,竞品公司的鸭子类型比我们多,不仅有红蓝绿鸭,还有模型鸭,木头鸭甚至黄金鸭,功能比我们强大,有些会飞,有些能发出“叽叽”声,有些能喷火,有些能射出子弹,抢了不少市场份额。我们需要迎头赶上。
变化:有些鸭子会飞,有些不会,木头鸭不会叫,模型鸭会叽叽叫,有些是呱呱叫。
方案1、Duck父类中加入fly()方法,会飞的继承,不会飞的覆盖,如fly(){//什么都不做}
。弊端:以后每新增一个子类,都要覆盖fly()方法。quack()方法也一样。
方案2、将fly()
、quack()方法抽象成Flyable、Quackable接口。会飞的鸭子实现Flyable接口并实现fly()方法,Quackable同样处理。乍一看,用接口处理确实提高的灵活性。想要什么功能就实现什么接口。但也造成另外一个灾难:若以后fly()的逻辑要更改,比如从直线飞行变成曲线飞行。实现接口的子类多少决定了你加班的时长。而且针对fly()这个动作子类中应该会有大量的重复代码。
要想解决此问题,需要引出一个设计原则:找出应用可能变化之处,把他们独立出来,不要和那些不需要变化的混在一起
此案例中。鸭子的基本属性不变,行为发生改变。所以,我们应该把行为,从鸭子类中独立出来。
此处有fly()和quack()行为有变化(其他的类似,此处仅以这两个举例),我们可以把其提取到一个类中。不过为了进一步分离,我们甚至可以分离到两个类中:一个Fly类(或接口)和一个Quack类(或接口):
//叫 接口public interface Quack { void quack();}//不同的叫声可以有不同的实现class QuackBehaviorGua implements Quack{ @Override public void quack() { System.out.println("我是这样叫的:呱呱呱"); }}class QuackBehaviorGi implements Quack{ @Override public void quack() { System.out.println("我是这样叫的:叽叽叽"); }}//飞 接口同样如此 (略)//这样设计,能让飞、叫这样的行为跟鸭子分离开。若此时有别的模型,鸡、牛等,也可以使用这些代码,达到复用。而且新增行为不会影响到原来的代码。厉害了我的哥
此时只需要把鸭子和行为关联起来,即在鸭子的内部,需要能够调用fly()或者quack()方法。不过首先,我们需要能够设置鸭子的行为,重构鸭子代码:
//鸭子超类public abstract class Duck { //鸭子类型 String duckName="Duck"; //负责处理不同行为,需要持有这些委托的引用,而且是接口类型 Fly flyBehavior; Quack quackBehavior; //构造器需要传入行为类(可以通过构造器参数传入,也可以在子类其他构造方法中实现) public Duck(String duckName,Fly fly,Quack quack) { this.duckName = duckName; this.flyBehavior = fly; this.quackBehavior = quack; } //不用管具体怎么飞,只要能飞就行 public void CommonFly(){ flyBehavior.fly(); } //不用管具体怎么叫,只要能叫就行 public void CommonQuack(){ quackBehavior.quack(); }}
此处引出另一个设计原则:针对接口编程,而不是针对实现编程
此例中飞、叫的行为,在客户端(Duck)中不用关心具体的实现是什么,只需知道有这么一个接口能实现需要的功能。直线或曲线、呱呱或叽叽在运行时确定而不是编译器(即代码没有写死)。
至此,我们需要在创造新的鸭子时,指定它拥有的行为是怎样的,直线飞:则实现一个直线飞的行为类传递到构造方法,曲线飞、呱呱叫、叽叽叽叫同样如此:
public static void main(String[] args) { String name = "直线飞、呱呱叫鸭"; //直线飞行 Fly flybehavior = new FlyBehaviorZhi(); //呱呱叫 Quack quackbehavior = new QuackBehaviorGua(); GreenDuck greenDuck ; greenDuck = new greenDuck(name,flybehavior,quackbehavior); greenDuck.display(); greenDuck.CommonFly(); greenDuck.CommonQuack(); System.out.println("-------------------"); name = "曲线飞、叽叽叫鸭"; flybehavior = new FlyBehaviorQu(); quackbehavior = new QuackBehaviorGi(); RedDuck redDuck = new RedDuck(name,flybehavior,quackbehavior); redDuck.display(); redDuck.CommonFly(); redDuck.CommonQuack(); }控制台输出:我是:直线飞、呱呱叫鸭我是这样飞的:直线我是这样叫的:呱呱呱-------------------我是:曲线飞、叽叽叫鸭我是这样飞的:曲线我是这样叫的:叽叽叽
至此,我们成功分离了行为和鸭子类。极大提高程序扩展性和复用性。
三、策略模式在JDK中的应用
学习设计模式最好的方式之一是分析其他类库中的使用案例,而JDK是最好的选择,因为你不仅可以了解设计模式,还能更深入的理解JDK。
JDK中应用策略模式的有:
● java.util.Comparator#compare()
● javax.servlet.http.HttpServlet
● javax.servlet.Filter#doFilter()
//JDK1.2中加入的java.util.Comparator的源码public interface Comparator<T> {//包含两个抽象方法 int compare(T o1, T o2); boolean equals(Object obj);}
使用场景之一是集合的排序中算法中。 java.util.Collections.sort(java.util.List, java.util.Comparator)
API中可以传入一个Comparator实现compare(T o1, T o2)方法的实现类,在最底层的调用中调用compare方法来比较元素大小实现排序。
四、总结
策略模式定义一组算法,并把其封装到一个对象中。然后在运行时,可以灵活的使用其中的一个算法。对于调用算法的客户端来说,不用关心最终是什么样的算法来运行。只需要知道运行了一个满足需求的算法即可。因为算法做了封装,且与客户端分离,可以做到复用。
- Head First设计模式—策略模式
- Head First 设计模式学习笔记 ——策略模式
- Head First 设计模式学习——策略模式
- Head First设计模式学习—模板方法、策略模式
- 《Head First 设计模式》学习笔记——策略模型
- 《Head First 设计模式》模式1——策略模式
- Head First设计模式——策略设计模式
- Head First 设计模式 —— 策略设计模式
- "Head First 设计模式"读书笔记——策略模式
- 《Head First设计模式》读书笔记1——策略模式
- Head First 设计模式——策略模式(Strategy Pattern)
- 读书笔记1:Head First设计模式——策略模式
- head first设计模式(一)——策略模式
- 策略模式详解——参考《Head First设计模式》
- Head First设计模式——策略模式
- Head First设计模式学习笔记(1)——Duck与策略模式
- head first设计模式学习笔记(一)——策略模式 C++实现
- Head First设计模式学习之一《策略模式》
- 特征工程与文本处理
- Redis监控技巧
- APP发数据到ESP8266这个WiFi模块数据发生错乱
- MATLAB入门教程
- 什么时候使用分析器
- Head First 设计模式学习——策略模式
- JS 日期相减
- 三十、html5本地存储的五种方案
- 在Action中以Struts2的方式输出JSON数据
- tf.expand_dims和tf.squeeze函数
- ADO.NET之SqlDataAdapter
- 磁疗是真的吗?磁场对心血管系统的作用
- NvrSDK交接文档
- RxJava2 / RxAndroid2操作符skip