Head First从鸭子分析——策略模式

策略模式

文中部分内容抄自于:https://blog.csdn.net/qq_39588630/article/details/80434240

  1. 这篇博客的由来
  2. 策略模式介绍
  3. 从鸭子分析策略模式
  4. 分析鸭子案例的角色
这篇博客的由来

最近在看一本书籍 《Head First 设计模式》,好吧,广受好评的书籍一般也不会令人失望。他被描述为 由四人组所著的**《设计模式》**的白话版,这里强烈推荐一下这本书吧,写的确实很幽默风趣。安利安利!

策略模式介绍

1. 什么是策略模式

策略模式(Pattern:Strategy)属于行为型模型,是指对一系列的算法定义,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

2. 策略模式包含的角色及其职责:

  • 抽象策略角色[Strategy]:策略类,定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,
    Context使用这个接口调用不同的算法,一般使用接口或抽象类实现。
  • 具体策略类[ConcreteStrategy]:实现了Strategy定义的接口,包装了相关的算法和行为,提供具体的算法实现。
  • 上下文角色[Context]:持有一个策略类的引用,最终给客户端调用。
    ①需要使用ConcreteStrategy提供的算法。
    ②内部维护一个Strategy的实例。
    ③负责动态设置运行时Strategy具体的实现算法。
    ④负责跟Strategy之间的交互和数据传递。*

3. 策略模式的特点

(1)优点:

   ①策略模式提供了管理相关的算法族的办法。策略类的等级结构定义了一个算法或行为族。恰当使用继承可以把公共的代码转移到父类里面,从而避免重复的代码。

   ②策略模式提供了可以替换继承关系的办法。继承可以处理多种算法或行为。如果不是用策略模式,那么使用算法或行为的环境类就可能会有一些子类,每一个子类提供一个不同的算法或行为。但是,这样一来算法或行为的使用者就和算法或行为本身混在一起。决定使用哪一种算法或采取哪一种行为的逻辑就和算法或行为的逻辑混合在一起,从而不可能再独立演化。继承使得动态改变算法或行为变得不可能。

  ③使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护,它把采取哪一种算法或采取哪一种行为的逻辑与算法或行为的逻辑混合在一起,统统列在一个多重转移语句里面,比使用继承的办法还要原始和落后。

(2)缺点:

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

   ②策略模式造成很多的策略类,每个具体策略类都会产生一个新类。有时候可以通过把依赖于环境的状态保存到客户端里面,而将策略类设计成可共享的,这样策略类实例可以被不同客户端使用。换言之,可以使用享元模式来减少对象的数量。

4. 策略模式的应用场景

   ①多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
   ②需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
   ③对客户隐藏具体策略(算法)的实现细节,彼此完全独立。

5.UML图
在这里插入图片描述

从鸭子分析策略模式

首先我们来定义一个场景:
模拟鸭子游戏:
游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫
在这里插入图片描述
所有鸭子的不同点:即外观不同,所以用了抽象方法,使得继承时必须重写抽象方法来定义鸭子的外观

但是现在的需求发生改变
现在需要部分鸭子具有会飞的动作,动作鸭子不发出瓜瓜的叫声,而发出其他声音,部分鸭子在原来拥有不会飞的行为,在某些特定的场合可以扩展成会飞的鸭子

从这里分析的话,也就是说,不同的鸭子会具有不同的行为,且鸭子的行为会根据的场合发生改变。抽出不同点。

第一个解决方法:在基类中添加fly(),让所有的子类去实现。造成的问题,不该有fly()方法的鸭子也无法幸免,而且子类还要重复重写quark()方法,这样做的话,如果要让原有的行为发生改变是很难的

这样一来造成的问题是:如果基类有已经定义的很多行为,子类继承的时候也会继承这些行为。如果子类的行为有变的话,就只能覆盖基类的行为方法。
在这里插入图片描述
第二个解决方法:将鸭子的fly()行为和quark()叫的行为取出来,封装接口,让子类去实现接口。造成的问题,有飞的行为和叫的行为的子类鸭子都要实现接口,会出现很多重复的代码。依然不能解决要根据场景变化行为的需求。

在这里插入图片描述

设计原则:找出应用中可能需要变化之处,把他们独立出来,不要和那些不需要变化的代码混在一起

策略模式
第三个解决方法:还是将飞和叫的行为抽成成接口,但不用鸭子类做实现,而用其他行为类来实现,而鸭子只要拥有行为类的对象即可(这里强调一句,面向接口编程),而具体鸭子应该拥有什么行为对象,由鸭子子类自己决定。

策略模式(Pattern:Strategy)属于行为型模型,是指对一系列的算法定义,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

关键在于:鸭子现在将自己的行为的具体实现委托给别人,而不在Duck类或其子类做实现。

在这里插入图片描述鸭子案例策略模式的全局观
在这里插入图片描述

从鸭子案例的代码分析

抽象策略角色:飞行行为接口:FlyBehavior 呱呱叫行为接口:QuarkBehavior
具体策略类:飞行行为实现类:FlyWithWing FlyNoWay 呱呱叫行为实现类:Quark(呱呱叫) Squeak(哇哇叫)MuteQuack(不叫)
上下文对象:鸭子类 Duck(维护策飞行,叫法的实例,运行策略类的实现算法,和策略类进行数据传递)

  • 鸭子类Duck
public abstract class Duck {
	protected FlyBehavior flyBehavior;
	protected QuarkBehavior quarkBehavior;
	/**
	 * 会游泳
	 */
	public void swim(){
		
	}
	/**
	 * 因为每一只鸭子的外观不同,所以这里用抽象方法
	 */
	public abstract void display();
	public void performFly(){
		flyBehavior.fly();
	}
	public void performQuark(){
		quarkBehavior.quark();
	}
	public void setFlyBehavior(FlyBehavior f){
		flyBehavior = f;
	}
	public void setQuarkBehavior(QuarkBehavior q){
		quarkBehavior = q;
	}
}

抽象策略角色 飞行行为 和 叫法行为
FlyBehavior

public interface FlyBehavior {
	public abstract void fly();
}

QuarkBehavior

public interface QuarkBehavior {
public abstract void quark();
}

具体策略类 飞行行为实现类
FlyWithWing 用翅膀飞的行为类

public class FlyWithWing implements FlyBehavior {

	@Override
	public void fly() {
		// TODO Auto-generated method stub
		System.out.println("我能飞");
	}

}

FlyNoWay 不会飞的行为类

public class FlyNoWay implements FlyBehavior {

	@Override
	public void fly() {
		// TODO Auto-generated method stub
		System.out.println("我不能飞");
	}

}

上下文对象子类
RedHeadDuck 红头鸭

public class RedHeadDuck extends Duck{

	@Override
	public void display() {
		// TODO Auto-generated method stub
		System.out.println("我是一只红头鸭");
	}

	public RedHeadDuck(){
		flyBehavior = new FlyWithWing();
		quarkBehavior = new Quark();
	}

}

这里总结一下使用策略类需要注意的点,
1、上下文对象(鸭子类)有哪些行为。(这样才能确定有哪些抽象策略接口和具体策略类)
2、鸭子子类必须明确自己的行为(可以替换)。(这样才能决定维护哪个具体策略类的对象,使用具体算法类的方法)
3、策略类和上下文对象完全独立

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值