设计模式篇之——策略设计模式
Posted 风清扬逍遥子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式篇之——策略设计模式相关的知识,希望对你有一定的参考价值。
策略模式在实际开发过程中其实非常的常见,那么本章也从原始需求扩展到设计模式上是如何更好的落地的。
1、定义
分别封装行为接口,实现算法族,父类里放行为接口对象,在子类里具体设定行为对象。一个类的行为或其算法可以在运行时更改,这种类型的设计模式属于行为型模式。
定义总是模糊的,用大白话来说,我们把每个要做的事情称为【行为】,每一个行为都应该被抽象成一个一个接口,和这些行为的使用者进行解耦。举个例子,比如鸭子这个抽象父类存在很多的行为方法,鸭子会游泳,会叫,会很多本领;每个不同的鸭子都要去继承这个抽象鸭子父类,每个鸭子叫声不同,需要对父类的叫方法进行重写。看起来是没问题,如果增加了额外的需求,比如鸭子要飞,那么很可能所有的子类都要去必须要重写飞,但是不是所有鸭子都会飞。一旦鸭子种类多起来,改动就会很大,我们需要的效果是,具体要哪个鸭子做什么,不应该所有的鸭子都被影响。
既然很离谱,那么我们分析下,变动的东西是不是一些特性?比如飞,不是所有的鸭子都会飞,不是所有的鸭子都会跳舞(比如唐老鸭),我们假定每个鸭子都会游泳。那么抽离出来个性,不变的是共性,即抽象是共性,接口是特性。
灵魂画师又来了:
2、案例讲解
组成部分我就不说了,上面的图就表明了哪些组成部分,正式进入需求:
产品经理:那个老王,你帮我做个功能,我要两个鸭子,黑鸭和红鸭子,这两个鸭子会叫、会飞、会游泳哈,区别是黑鸭和红鸭子的颜色不一样(这不是废话么)。
这个时候我们用OOP思想(面向对象编程),定义一个鸭子父类。
/** * @Description: 设计一个抽象类鸭子 * @title: Duck * @Author Star_Chen * @Date: 2021/12/21 22:36 * @Version 1.0 */ public abstract class Duck public Duck() /** * @Date: 2021/12/21 22:39 * @Description: 抽象的自我介绍颜色方法让子类实现 */ public abstract void perform(); /** * @Date: 2021/12/21 22:39 * @Description: 本抽象父类中自己实现游泳方法,子类也可以自己实现 */ public void swim() System.out.println("我是个鸭子,会游泳~"); /** * @Date: 2021/12/21 22:46 * @Description: 本抽象父类中自己实现叫的方法,子类也可以自己实现 */ public void quack() System.out.println("我是鸭子,我会叫呱呱呱!");
来一个红鸭子
/** * @Description: 红鸭子 * @title: RedDuck * @Author Star_Chen * @Date: 2021/12/21 22:40 * @Version 1.0 */ public class RedDuck extends Duck @Override public void perform() System.out.println("我是红色的鸭子!");
再来一个黑鸭子
/** * @Description: 黑鸭子 * @title: BlackDuck * @Author Star_Chen * @Date: 2021/12/21 22:41 * @Version 1.0 */ public class BlackDuck extends Duck @Override public void perform() System.out.println("我是黑色的鸭子!");
来个Main函数调用
/** * @Description: * @title: DuckMain * @Author Star_Chen * @Date: 2021/12/21 22:44 * @Version 1.0 */ public class DuckMain public static void main(String[] args) RedDuck redDuck = new RedDuck(); BlackDuck blackDuck = new BlackDuck(); redDuck.quack(); redDuck.perform(); redDuck.swim(); System.out.println("<==========================>"); blackDuck.quack(); blackDuck.perform(); blackDuck.swim();
输出结果很简单
我是鸭子,我会叫呱呱呱! 我是红色的鸭子! 我是个鸭子,会游泳~ <==========================> 我是鸭子,我会叫呱呱呱! 我是黑鸭子! 我是个鸭子,会游泳~
这个时候产品经理说,对了给我再来两个鸭子,让鸭子飞,但是呢我只让黑鸭子飞和xxx鸭子飞,对了我还要不同的鸭子叫的不一样,红鸭子geigeigei,黑鸭子一给我里giaogiao...
想一下,如果在父类写fly方法,定义成抽象方法,那么所有继承父类的鸭子,都要添加fly方法,但是不是所有鸭子都会飞呀,违背逻辑。那如果不定义成抽象方法,定义成父类的普通方法,子类的所有鸭子,要想实现就必须重写父类的fly,或者要么就调用父类的fly,但是实际中,比如北京烤鸭都没有飞的概念,怎么能让子类具有这样的方法呢?显然这是不可能的
父类挖的一个坑,每个子类都要来填,增加工作量,复杂度O(N^2)。不是好的设计方式。这时候,我们应该考虑哈继承的弊端了。使用接口就可以很好的解决这个问题。(思路:继承是实现共性,减少代码的重复。接口是实现特性。)
我们采用策略模式来做
分析:
1、项目变化与不变部分,提取变化部分,抽象成接口+实现;(抽象是共性,接口是特性)
2、鸭子那些功能是会根据新需求变化的?叫声、飞行
定义两个变化的行为接口
FlyBehavior和QuackBehavior
定义飞的行为
/** * @Description: * @title: FlyBehavior * @Author Star_Chen * @Date: 2021/12/21 23:53 * @Version 1.0 */ public interface FlyBehavior /** * @Date: 2021/12/21 23:54 * @Description: 飞的行为 */ void fly();
定义叫的行为
/** * @Description: * @title: QuackBehavior * @Author Star_Chen * @Date: 2021/12/22 0:00 * @Version 1.0 */ public interface QuackBehavior /** * @Date: 2021/12/22 0:01 * @Description: 叫的行为 */ void quack();
那么Duck父类我们就要调整,只要涉及到个性的东西,比如fly,quack,都要以行为去做变更。
/** * @Description: 设计一个抽象类鸭子 * @title: Duck * @Author Star_Chen * @Date: 2021/12/21 22:36 * @Version 1.0 */ public abstract class Duck //具备抽象行为 FlyBehavior flyBehavior; QuackBehavior quackBehavior; /** * @Date: 2021/12/22 0:07 * @Description: 实例化对象时可以动态的改变对象的行为(比继承灵活性强) */ public void setFlyBehavior(FlyBehavior flyBehavior) this.flyBehavior = flyBehavior; /** * @Date: 2021/12/22 0:07 * @Description: 实例化对象时可以动态的改变对象的行为(比继承灵活性强) */ public void setQuackBehavior(QuackBehavior quackBehavior) this.quackBehavior = quackBehavior; /** * @Date: 2021/12/21 22:46 * @Description: 本抽象父类中自己实现叫的方法,子类也可以自己实现 */ public void quack() // System.out.println("我是鸭子,我会叫呱呱呱!"); quackBehavior.quack(); public void fly() flyBehavior.fly(); /** * @Date: 2021/12/21 22:39 * @Description: 抽象的自我介绍颜色方法让子类实现 */ public abstract void perform(); /** * @Date: 2021/12/21 22:39 * @Description: 本抽象父类中自己实现游泳方法,子类也可以自己实现 */ public void swim() System.out.println("我是个鸭子,会游泳~");
叫行为的实现类
/** * @Description: giaogiao的叫 * @title: GiaoQuackBehavior * @Author Star_Chen * @Date: 2021/12/22 0:01 * @Version 1.0 */ public class GiaoQuackBehavior implements QuackBehavior @Override public void quack() System.out.println("一给我里giaogiao的叫!"); /** * @Description: geigei的叫 * @title: UglyQuackBehavior * @Author Star_Chen * @Date: 2021/12/22 0:01 * @Version 1.0 */ public class UglyQuackBehavior implements QuackBehavior @Override public void quack() System.out.println("geigei的叫!");
飞行为的实现类
/** * @Description: 低飞 * @title: LowFlyBehavior * @Author Star_Chen * @Date: 2021/12/21 23:55 * @Version 1.0 */ public class LowFlyBehavior implements FlyBehavior @Override public void fly() System.out.println("鸭子会低飞!"); /** * @Description: 高飞 * @title: HighFlyBehavior * @Author Star_Chen * @Date: 2021/12/21 23:54 * @Version 1.0 */ public class HighFlyBehavior implements FlyBehavior @Override public void fly() System.out.println("鸭子会高飞!");
我们模拟鸭子主函数,还是实现上面新增的需求:
红鸭子叫geigeigei,黑鸭子叫一给我里giaogiao
红鸭子高飞,黑鸭子低飞
在调用方面我们就这么写:
/** * @Description: * @title: DuckMain * @Author Star_Chen * @Date: 2021/12/21 22:44 * @Version 1.0 */ public class DuckMain public static void main(String[] args) //父类为Duck,屏蔽了超类的差别性 Duck redDuck = new RedDuck(); Duck blackDuck = new BlackDuck(); redDuck.perform(); redDuck.fly(); System.out.println("<==========================>"); blackDuck.perform(); blackDuck.quack();
输出结果:
我是红色的鸭子! 鸭子会高飞! <==========================> 我是黑色的鸭子! 一给我里giaogiao的叫!
如果我想自定义在对象的实例化的时候同时对行为进行重写,那么可以通过setBehavior来实现。
/** * @Description: * @title: DuckMain * @Author Star_Chen * @Date: 2021/12/21 22:44 * @Version 1.0 */ public class DuckMain public static void main(String[] args) //父类为Duck,屏蔽了超类的差别性 Duck redDuck = new RedDuck(); Duck blackDuck = new BlackDuck(); redDuck.perform(); redDuck.setFlyBehavior(new FlyBehavior() @Override public void fly() System.out.println("红鸭子我不飞了"); ); redDuck.fly(); System.out.println("<==========================>"); blackDuck.perform(); blackDuck.setQuackBehavior(new QuackBehavior() @Override public void quack() System.out.println("黑鸭子我不叫了"); ); blackDuck.quack();
以上代码都是java中的基础知识。运用设计模式中的策略模式。把变化的部分提取出来变为接口+实现。其中,main方法中,父类为Dock基类,是为了屏蔽子类的超类Dock的差别性。Duck类中的SetQuackBehavoir()方法,灵活的让实例化对象灵活的改变对象的行为。比如,绿头鸭,使用了SetQuackBehavoir()方法,定制了自己的quck()方法。因为不是每只鸭都能叫的,叫的是当前鸭的特性。
3、总结
我们可以假想,一个鸭子可以有很多子类。如果把方法全部写在父类。子类都会继承它,对于不同的子类,有些方法违反了逻辑成为了冗余(不应该出现在子类中)。
那是不是就没有缺点了呢?当然有,在调用端必须要知道所有的这些行为,否则无法在任何场合决定调用行为的什么结果,而对于很多场景需求中,我们并不明确的知道下一个需求是什么,即不知道会存在多少个行为策略类,因为每个行为策略都会有个类出现,如果被多处调用,就必须要知道所有的策略场景,维护也不是很方便。
下一章节我们讲述装饰者模式。
以上是关于设计模式篇之——策略设计模式的主要内容,如果未能解决你的问题,请参考以下文章