在引入策略模式之前。先用一个小项目解释为什么要有策略模式。这是一个模拟鸭子的小游戏。最开始需求为,所有的鸭子都会叫以及都会游泳且叫声游泳姿势都一样。因此我们用原始OO的思维去编写代码时很容易做到

public abstract class Duck{
public void yuck(){
System.out.println("嘎嘎叫");
} public void swim(){
Systrm.out.println("i am swim");
} public abstract void display();
}

也就是封装一个超类。然后设置两个子类通用的方法,叫和游泳。此时再编写程序需要的各种不同的鸭子子类。他们都能够叫和游泳。此时编写一个测试类。

public class Test{

   public static void main(String[] args){
Duck gDuck = new GreenDuck();
Duck rDuck = new RedDuck(); gDuck.display();
gDuck.yuck();
gDuck.swim(); rDuck.display();
rDuck.yuck();
rDuck.swim(); }
}
public class GreenHeadDuck extends Duck{

   @override
public void display(){
System.out.println("绿头鸭");
}
}
public class RedHeadDuck extends Duck{

    @override
public void display(){
System.out.println("红头鸭");
}
}

测试类的结果为:绿头鸭   嘎嘎叫  i am swim    红头鸭  嘎嘎叫  i am swim。这个完全没有问题。好的,现在游戏上线,市场火热,新需求来了。鸭子要会飞。那我们继续用OO的思维去想,

那么可以给超类Duck中设置一个通用的fly方法。让所有的鸭子都会飞。让我们把Duck的代码改一下。改成下面的

public abstract class Duck{
public void yuck(){
System.out.println("嘎嘎叫");
} public void swim(){
Systrm.out.println("i am swim");
} public abstract void display(); public void fly(){
System.out.println("i am flying");
}
}

好的,现在的确实现了鸭子会飞的功能,但是这也引起了一些问题。因为事实上不是所有的鸭子都会飞。你在超类中定义了方法。影响的是全部的子类。容易导致溢出的问题,也就是。有些子类不需要。但是你给它强制加了。好,那如果现在有些子类不会飞。我们继续用OO的思维去解决。那么可以在不会飞的子类中用重写去覆盖掉父类给他的方法。我们加入绿头鸭不会飞,我们修改以下绿头鸭的代码如下

public class GreenHeadDuck extends Duck{

   @override
public void display(){
System.out.println("绿头鸭");
} @override
public void fly(){
System.out.println("i can't fly")
}
}

好,可以通过在子类中覆盖来实现有些鸭子会费飞有些不会飞这个需求。但是在我们这个例子中鸭子种类少,这么玩没有关系。但如果由2000种鸭子呢。难道你要一个一个的去覆盖子类中的方法。

很明显就能看出单纯的面向对象,也就是OO的思想在设计这个需求的时候并不完善。那也有些人说,那我不在超类Duck中定义fly的方法。我在子类中去定义,谁会飞我就给谁定义。这其实和上面一样。如果子类基数大。那么你写的重复代码也会很多。那么OO中的代码复用就完全没有体现出来了。

因此,我们分析以下上面OO设计的问题所在。为什么会说这个设计不好。原因是在于它的行为和客户类没有分离。也就是“干什么” 和 “怎么干”没有分离开来。例如上面的例子。客户类中的fly是干什么,但是怎么干,fly里面怎么定义代码,他们没有分离。全部堆在客户类中,耦合度太高。导致,你的“怎么干“放在客户类中定义,无论你放在超类还是子类。都容易出问题,因此就导出了策略模式这个概念

策略模式是干嘛的,是去耦合的。它所做的就是把”干什么“和”怎么干“分离开来。把”怎么干“封装到一个策略接口或者抽象类中。然后编写一系列的怎么干算法封装起来。客户类中只需要调用这个算法就ok了

策略模式的两种标准点的定义:

图视:

   环境对象(Context):该类中实现了对抽象策略中定义的接口或者抽象类的引用。
      抽象策略对象(Strategy):它可由接口或抽象类来实现。
      具体策略对象(ConcreteStrategy):它封装了实现同不功能的不同算法。

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换,策略模式让算法独立于使用它的客户而独立变化。

使这些算法在客户端调用它们的时候能够互不影响地变化。


策略模式:分别封装行为接口,实现算法族,超类中放行为接口对象。在子类中的构造方法中去实例化行为对象。原则就是:分离变化部分,封装接口,基于接口编程各种功能族,让行为算法的变化独立于算法的使用者。

使用策略模式的步骤:

1.分析变化的行为,拿出来进行封装成一个策略接口或者抽象类。然后定义该行为的多种实现算法。形成算法族

2.在客户超类中放策略接口。

3.在子类的构造方法中去具体实例化具体的策略算法

 


下面让我们看看用策略模式修改模拟鸭子的这个项目。

1.封装变化的部分。当前鸭子的变化行为为fly。有些会飞,有些不会飞。有些会飞的好,有些飞的不好。因此封装fly这个行为为一个策略接口。FlyObject

public interface FlyObject{
void fly();
}

接下来,编写项目中实现fly这个行为的多种算法。例如不会飞 nofly,飞的好,good fly,飞得不好 badfly。这三个行为族

public class NoFly implements FlyObject{

   @override
public void fly(){
System.out.println("不会飞")
}
}
public class GoodFly implements FlyObject{

    @override
public void fly(){
System.out.println("Good fly");
}
}
public class BadFly implements FlyObject{

   @override
public void fly(){
System.out.println("bad fly");
}
} 之后在客户类的超类也就是Duck中放置策略接口,也就是FlyObject。
package com.lyh.strategy.duck;

import com.lyh.strategy.flybehavior.FlyBehavior;
import com.lyh.strategy.yuckbehavior.YuckBehavior; public abstract class Duck { FlyBehavior flybehavior;
YuckBehavior yuckbehavior;
public Duck() { }; public void fly() {
flybehavior.fly();
} public void yuck() {
yuckbehavior.yuck();
} public abstract void display(); public void setFlyBehavior(FlyBehavior fb) {
this.flybehavior = fb;
} public void setYuckBehavior(YuckBehavior yb) {
this.yuckbehavior = yb;
} //这个方法子类通用,不会变化
public void swim() {
System.out.println("i am swim");
}
}

在客户类的子类中去实例化具体的策略接口算法。
package com.lyh.strategy.duck;

import com.lyh.strategy.flybehavior.GoodFlyBehavior;
import com.lyh.strategy.yuckbehavior.GaGaYuckBehavior; public class GreenHeadDuck extends Duck { public GreenHeadDuck() {
flybehavior = new GoodFlyBehavior();
yuckbehavior = new GaGaYuckBehavior();
}
@Override
public void display() {
System.out.println("绿头鸭");
} }

package com.lyh.strategy.duck;

import com.lyh.strategy.flybehavior.NoFlyBehavior;
import com.lyh.strategy.yuckbehavior.NoYuckBehavior; public class RedHeadDuck extends Duck { public RedHeadDuck() {
flybehavior = new NoFlyBehavior();
yuckbehavior = new NoYuckBehavior();
}
@Override
public void display() {
System.out.println("红头鸭");
} }
因此,通过这种方式,能够实现客户类中的行为和客户类相分离。把变化的行为独立出来。抽象成接口。这样如果有新的行为需求,可以直接在行为族中加入。如果有新的客户类对象。也可以让他们
具体选择的去实现行为族。达到降低耦合度的目的。 但是,策略模式也有缺点。也就是客户类必须了解策略行为族中的每一个算法的实现以及原理。只有全部知晓。客户类才能去区别调用他们而不会出错。这时策略模式必须要做到的一点。

我们什么时候使用策略模式:这个没有具体的需求。一般而言。如果你定义的抽象父类中的一些方法是可变的。子类中不一定需要这些方法的时候。建议用策略模式将变化的行为封装起来。这其实也是一种方法的封装。你将行为封装,那么其他的类也可以去使用。

OOAD之策略模式(1)的更多相关文章

  1. 8.6 GOF设计模式四: 策略模式… Strategy Pattern

    策略模式… Strategy Pattern  在POS系统中,有时需要实行价格优惠, 该如何处理?  对普通客户或新客户报全价  对老客户统一折扣5%  对大客户统一折扣10%  注:课件 ...

  2. javascript设计模式:策略模式

    前言 策略模式有效利用组合.委托.多态等技术和思想,可以有效避免多重条件选择语句. 策略模式对开放-封闭原则提供了很好的支持,将算法封装在strategy中,使得他们易于切换.理解.扩展. 策略模式中 ...

  3. StrategyPattern (策略模式)

    /** * 策略模式 * @author TMAC-J * 根据环境的不同选择不同的策略,把策略用接口抽象出来 */ public class StrategyPattern { interface ...

  4. JAVA 设计模式之策略模式

    定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换. 类型:行为类模式 策略模式是对算法的封装,把一系列的算法分别封装到对应的类中,并且这些类实现相同的接口,相互之间可以替换.在前面说过 ...

  5. Java设计模式之策略模式(Strategy)

    前言: 最近一直在学习基于okHttp网络请求,学习的过程中就想起了之前项目中有这么一个需求不同的接口要采用不同的加密方式,比如登录之前要采用RSA加密,登录之后要采用AES加密,当时是采用靠传递一个 ...

  6. 设计模式(一):“穿越火线”中的“策略模式”(Strategy Pattern)

    在前段时间呢陆陆续续的更新了一系列关于重构的文章.在重构我们既有的代码时,往往会用到设计模式.在之前重构系列的博客中,我们在重构时用到了“工厂模式”.“策略模式”.“状态模式”等.当然在重构时,有的地 ...

  7. 《Head First 设计模式》之策略模式

    作者:Grey 原文地址:http://www.cnblogs.com/greyzeng/p/5915202.html 模式名称 策略模式(Strategy Pattern) 需求 模拟鸭子游戏,游戏 ...

  8. 学C#之设计模式系列笔记(1)策略模式

    一.借鉴说明 1.<Head First Design Patterns>(中文名<深入浅出设计模式>) 2.维基百科,策略模式,https://zh.wikipedia.or ...

  9. PHP 策略模式

    策略模式:定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换.本模式使得算法可独立于使用它的客户而变化.策略模式把对象本身和运算规则区分开来,其功能非常强大,因为这个设计模式本身的核心思想 ...

随机推荐

  1. Extjs入门-grid

    function rowdblclickFn(grid, rowIndex, e){//双击事件              var row = grid.store.getById(grid.stor ...

  2. 转: 谈JAVA_OPTS环境变量不起作用

    谈JAVA_OPTS环境变量不起作用 2016-6-14 11:12 最近在处理运行一个java应用时,老是出现java.lang.OutOfMemoryError: Java heap space. ...

  3. sed中引用变量

    sed 中引用变量 eval sed 's/string/$REPLACE/g' file awk 中引用变量 awk 在匹配字符串的时候,有时候需要需要引用变量. $pid= eval " ...

  4. npm安装删除模块以及cnpm淘宝镜像

    npm安装模块 [$ npm install xxx]利用 npm 安装xxx模块到当前命令行所在目录: [$ npm install -g xxx]利用npm安装全局模块xxx: npm 删除模块 ...

  5. 北斗卫星同步时钟(NTP网络时钟服务器)成功投运世界级工程港珠澳大桥

    北斗卫星同步时钟(NTP网络时钟服务器)成功投运世界级工程港珠澳大桥 北斗卫星同步时钟(NTP网络时钟服务器)成功投运世界级工程港珠澳大桥 本文由北京华人开创科技公司提供 原址在 http://www ...

  6. 关于ORM的浴室思考

    这是一个由EF群引发的随笔 平时在一个EF群摸鱼,日常问题可以归纳为以下几种: 这条sql用linq怎么写? EF可以调用我写的存储过程么? EF好慢啊一些复杂查询写起来好麻烦-- 为什么会有这些问题 ...

  7. Python 抽象篇:面向对象之高阶用法

    1.检查继承 如果想要查看一个类是否是另一个类的子类,可以使用内建的issubclass函数 如果想知道已知类的基类,可以直接使用特殊特性__bases__ 同时,使用isinstance方法检查一个 ...

  8. 二叉树——遍历篇(递归/非递归,C++)

    二叉树--遍历篇 二叉树很多算法题都与其遍历相关,笔者经过大量学习.思考,整理总结写下二叉树的遍历篇,涵盖递归和非递归实现. 1.二叉树数据结构及访问函数 #include <stdio.h&g ...

  9. 基于Grafana+SimpleJson的灵活报表解决方案

    在时序分析及监控展现领域,Grafana无疑是开源解决方案中的翘楚,其灵活的插件机制,支持各种漂亮的面板.丰富的数据源以及强大的应用.典型的面板有Graph.Text.Singlestat.PieCh ...

  10. 循序渐进之Spring AOP(2) - 基本概念

    学习AOP前要先了解几个重要术语:Joinpoint.Pointcut.Advice 仍然以改装车比喻,拿到心爱的汽车后想做改装,第一件事是什么?找到要改装的地方.车上可改装的地方很多,但每个人感兴趣 ...