文档章节

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

o
 onedotdot
发布于 2017/08/22 14:34
字数 1810
阅读 8
收藏 0
点赞 0
评论 0

  前段时间买了一本书《Head First设计模式》,看了第一章后才对设计模式的概念有少许了解:它其实是开发过程中很多前人的经验与智慧的总结,帮助你在开发时采取更好的方式去设计各个类、方法、以及它们之间的调用、实现方式,让代码保持灵活性的同时又能更好地复用。基于学过一块知识一定要用文字记录、总结、巩固,而不是走马观花的原则,趁最近终于有空,特将前一段时间看的关于“策略模式”的内容总结于此。

场景描述

A公司要做一套模拟鸭子的游戏,游戏中会出现各种鸭子,一边游泳戏水,一边呱呱叫,还有一些会飞。

方案1 继承

设计一个超类Duck,包含方法quack()、swim()、fly()分别模拟鸭子的叫、游泳、飞行等行为,再包含一个抽象类display(),用于展示各个鸭子不同的外观,让每个鸭子子类继承父类时实现display()。

弊端:

这样做的好处是,每个鸭子子类继承父类时就同时拥有了父类的方法,可以达到代码复用的目的,但是这样会使某些并不适合该行为的子类也具有该行为。如果某些子类鸭子,如“橡皮鸭”,它不具备某些功能(如飞行),它就不应该拥有这个飞行的功能。当然,你可以在子类中通过@Override覆盖这个方法。但是当各个不同的子类都需要通过覆盖修改不同的方法时,就会非常繁琐,而且容易出现纰漏,且这些新覆盖的方法不能被复用。如“橡皮鸭”只会叫不会飞,“木头鸭”不会叫也不会飞。以后每当有新的鸭子子类出现,你都要去检查并可能需要覆盖这些方法,想想都让人抓狂。

方案2 接口

在超类Duck中将quack()、fly()等可变的方法用接口Quackable(),Flyable()来代替,然后在每个鸭子子类中,如果具有“飞行”或“叫”这个功能就实现“飞行“或”叫“这个接口。

弊端:

代码无法复用,如果有100个子类,都具有飞行的行为,你就需要重复100次代码。

设计模式来帮忙

设计原则一:找出程序中可能需要变化的地方和不需要变化的地方,将它们独立开来。让系统中的某部分改变不会影响其他部分。

由于fly()和quack()会随着鸭子的不同而改变,所以把这两个行为从Duck类中分开,建一组新类来代表各个行为。

设计原则二:针对接口编程,而不是针对实现。

利用多态,针对超类型编程,执行时根据实际状况执行到真正的行为,不会被绑死在超类型的行为上。以前的做法是:行为来自超类的具体实现或是继承某个接口并由子类自行实现。这两种方法都捆绑于”实现“,无法方便地更改行为。现在我们利用接口代表每个行为,比如FlyBehavior,QuackBehavior,然后让各个行为类实现这些接口,然后在Duck类中只要定义这个接口的实例变量即可,这样在各个鸭子子类中如果想拥有某种特定的行为,只要用这个接口实例变量去引用具体的行为类即可。

设计原则三:多用组合,少用继承。

飞行和叫这两种不同的行为,我们分别为其建立两组不同的行为类,然后在Duck类中通过接口实例变量结合起来,这就是”组合“。使得系统具有很大的弹性,还可以”在运行时动态地改变行为“。

下面就是经过重新设计后的应用代码:

1.应用的目录结构图

2.接口FlyBehavior

1 public interface FlyBehavior {
2     public void fly();
3 }

 3.接口QuackBehavior

1 public interface QuackBehavior {
2     public void quack();
3 }

4.Fly行为的一个实现——FlyNoWay

1 public class FlyNoWay implements FlyBehavior{
2     @Override
3     public void fly() {
4         System.out.println("I can not fly.");
5     }
6 }

5.Fly行为的另一个实现——FlyWithWings

1 public class FlyWithWings implements FlyBehavior {
2     @Override
3     public void fly() {
4         System.out.println("I can fly!");
5     }
6 }

6.Fly行为的又另一个实现——FlyRocketPowered 

1 public class FlyRocketPowered implements FlyBehavior{
2     @Override
3     public void fly() {
4         System.out.println("I am flying with a rocket.");
5     }
6 }

7.父类Duck

复制代码

1 public abstract class Duck {
 2     FlyBehavior flyBehavior;
 3     QuackBehavior quackBehavior;
 4     public abstract void display();
 5     public void performFly(){
 6         flyBehavior.fly();
 7     }
 8     public void performQuack(){
 9         quackBehavior.quack();
10     }
11     public void setFlyBehavior(FlyBehavior fb){
12         this.flyBehavior = fb;
13     }
14     public void setQuackBehavior(QuackBehavior qb){
15         this.quackBehavior=qb;
16     }
17 }

复制代码

8.Duck的一个子类——绿头鸭MallardDuck

复制代码

1 public class MallardDuck extends Duck{
 2     public MallardDuck() {
 3         flyBehavior = new FlyWithWings();
 4         quackBehavior = new QuackWithGuaGua();
 5     }
 6 
 7     @Override
 8     public void display() {
 9         System.out.println("I am a MallardDuck.");
10         
11     }
12 }

复制代码

9.Duck的另一个子类——模型鸭ModelDuck

复制代码

1 public class ModelDuck extends Duck {
 2     public ModelDuck() {
 3         flyBehavior = new FlyNoWay();
 4         quackBehavior = new QuackNoWay();
 5     }
 6     
 7     @Override
 8     public void display() {
 9         System.out.println("I am a ModelDuck.");        
10     }
11 }

复制代码

10.应用模拟器(执行主类):MiniDuckSimulator

复制代码

1 public class MiniDuckSimulator {
 2 
 3     public static void main(String[] args) {
 4         Duck mallardDuck = new MallardDuck();
 5         mallardDuck.display();
 6         mallardDuck.performFly();
 7         mallardDuck.performQuack();
 8         Duck modelDuck = new ModelDuck();
 9         modelDuck.display();
10         modelDuck.performFly();
11         modelDuck.performQuack();
12         modelDuck.setFlyBehavior(new FlyRocketPowered());
13         modelDuck.performFly();
14     }
15 
16 }

复制代码

执行代码,得到的结果如下:

总结

策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。这是书里给出的策略模式的定义,依我个人的理解,这种模式的关键就是要将系统中的可变行为抽象出来,单独进行封装,用一组行为类(算法族)来实现该特定的接口,这样任何类(如Duck)如果想拥有这些算法族中的某个算法,都可以通过定义接口实例变量而拥有该整个算法族,在子类中再对该变量进行赋值。像这个例子里,通过定义了FlyBehavior,这样以后任何想有飞行行为的类如飞机,都可以调用这个接口及实现它的各个飞行类。且飞行行为的变化可以通过增加新的飞行类来实现,不会对其他部分(如quack()、display()等行为,亦或是已拥有某些特定飞行行为的对象)造成任何影响,即”算法的变化独立于使用算法的客户“。而且各个飞行行为之间也可以互相替换。即 setFlyBehavior(Flybehavior fb)

可见,设计模式的应用让整个项目的代码拥有了极大的灵活性,且达到了代码复用的效果。设计模式其实是一种设计上的思维方式,是前人的智慧和经验的总结,其真正的精髓不是看过就能学会的,还是需要在实际应用中不断地实践摸索,慢慢体会。

文章中如果有任何问题,欢迎大家指正。

本文转载自:http://www.cnblogs.com/znicy/p/5522422.html

共有 人打赏支持
o
粉丝 6
博文 320
码字总数 14350
作品 0
朝阳
C#设计模式(2)——简单工厂模式

一、引言   这个系列也是自己对设计模式的一些学习笔记,希望对一些初学设计模式的人有所帮助的,在上一个专题中介绍了单例模式,在这个专题中继续为大家介绍一个比较容易理解的模式——简单工...

技术小胖子
2017/11/08
0
0
JavaScript设计模式之观察者模式

前言 准备研究一下MVVM的一些东西,由于MVVM运用了观察者模式的思想,因此翻开了《JavaScript设计模式与开发实践》一书,将观察者模式学习了一遍,顺便有对一些常用的设计模式进行一些了解,...

Srtian
05/22
0
0
迈向大牛的重要一步——掌握设计模式

IT职场的小菜经常有这样的疑问: 为什么一个相似的功能,大牛一会儿就搞定,然后悠闲地品着下午茶逛淘宝;而自己加班加点搞到天亮还做不完。 为什么用户提出需求变更后,大牛只需潇洒地敲敲键...

一枚Sir
2015/04/10
0
0
Java 设计模式(14) —— 复合模式

一、复合模式 模式常一起使用,组合在一个设计解决方案中 复合模式在一个解决方案中结合两个或多个模式,能解决一般性或一系列的问题 二、示例 本次设计模式讲解中无代码示例,由于复合模式是...

磊_lei
05/26
0
0
Java设计模式之抽象工厂模式

抽象工厂模式特点 抽象工厂模式与工厂模式最大的区别是每个单独的产品是一个工厂模式,而多个不同产品交互则是抽象工厂模式,抽象工厂模式通常要运用工厂模式的方法。通常,工厂模式的所有产...

断桥残雪断桥残雪
2015/08/09
0
0
设计模式15——Template Method设计模式

Template Method模板方法设计模式定义一个操作中算法的骨架,将具体步骤的执行延迟到子类中实现。Java中的抽象类就是使用了模板方法设计模式。模板方法设计模式结构如下: 以文档处理为例,T...

小米米儿小
2014/01/24
0
0
(目录)设计模式(可复用面向对象软件的基础)

本系列“设计模式”博客使用Golang语言实现算法。所谓算法是指解决一个问题的步骤,个人觉得不在于语言。小弟只是最近学习Golang,所以顺带熟练一下语法知识,别无它意。 本系列博客主要介绍...

chapin
2015/01/13
0
0
状态与策略——审批操作的两种方案

审批操作是ERP或OA系统中必不可少的功能之一。这里介绍两种我设计的用于审批操作的方案,并借此就“状态模式”与“策略模式”提出一点自己的理解。 别问我为什么不使用工作流引擎等工具来实现...

winters1224
06/26
0
0
【软考学习】设计模式——解释器模式

【背景】 设计模式是非常重要的一块知识,每个设计模式都值得深入了解和学习。 【内容】 结构型设计模式总结: 解释器设计模式总结: 一、定义:给定一个语言,定义它的文法的一种表示,并定...

yym15732626210
01/31
0
0
简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别

转载:原地址http://www.cnblogs.com/zhangchenliang/p/3700820.html 简单工厂、工厂方法、抽象工厂、策略模式、策略与工厂的区别 结合简单示例和UML图,讲解工厂模式简单原理。 一、引子 话说...

法斗斗
05/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

前端基础

1. get请求传参长度的误区 误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的。 实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是...

wenxingjun
今天
0
0
Android 复制和粘贴功能

做了一回搬运工,原文地址:https://blog.csdn.net/kennethyo/article/details/76602765 Android 复制和粘贴功能,需要调用系统服务ClipboardManager来实现。 ClipboardManager mClipboardM...

她叫我小渝
今天
0
0
拦截SQLSERVER的SSL加密通道替换传输过程中的用户名密码实现运维审计(一)

工作准备 •一台SQLSERVER 2005/SQLSERVER 2008服务 •SQLSERVER jdbc驱动程序 •Java开发环境eclipse + jdk1.8 •java反编译工具JD-Core 反编译JDBC分析SQLSERVER客户端与服务器通信原理 SQ...

紅顏為君笑
今天
6
0
jQuery零基础入门——(六)修改DOM结构

《jQuery零基础入门》系列博文是在廖雪峰老师的博文基础上,可能补充了个人的理解和日常遇到的点,用我的理解表述出来,主干出处来自廖雪峰老师的技术分享。 在《零基础入门JavaScript》的时...

JandenMa
今天
0
0
linux mint 1.9 qq 安装

转: https://www.jianshu.com/p/cdc3d03c144d 1. 下载 qq 轻聊版,可在百度搜索后下载 QQ7.9Light.exe 2. 去wine的官网(https://wiki.winehq.org/Ubuntu) 安装 wine . 提醒网页可以切换成中...

Canaan_
今天
0
0
PHP后台运行命令并管理运行程序

php后台运行命令并管理后台运行程序 class ProcessModel{ private $pid; private $command; private $resultToFile = ''; public function __construct($cl=false){......

colin_86
今天
1
0
数据结构与算法4

在此程序中,HighArray类中的find()方法用数据项的值作为参数传递,它的返回值决定是否找到此数据项。 insert()方法向数组下一个空位置放置一个新的数据项。一个名为nElems的字段跟踪记录着...

沉迷于编程的小菜菜
今天
1
1
fiddler安装和基本使用以及代理设置

项目需求 由于开发过程中客户端和服务器数据交互非常频繁,有时候服务端需要知道客户端调用接口传了哪些参数过来,这个时候就需要一个工具可以监听这些接口请求参数,已经接口的响应的数据,这种...

银装素裹
今天
0
0
Python分析《我不是药神》豆瓣评论

读取 Mongo 中的短评数据,进行中文分词 对分词结果取 Top50 生成词云 生成词云效果 看来网上关于 我不是药神 vs 达拉斯 的争论很热啊。关于词频统计就这些,代码中也会完成一些其它的分析任...

猫咪编程
今天
0
0
虚拟机怎么安装vmware tools

https://blog.csdn.net/tjcwt2011/article/details/72638977

AndyZhouX
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部