这是我第一次写文章,可能有写得不好的地方请大佬指正

可能这会是一篇长期连载的设计模式系列哈哈哈

搬至 head First 设计模式 ,这本书真的非常有意思和易懂

首先 要从设计模式入门 ,需要先看一个简单的模拟鸭子应用开始

JOE的公司做了一套相当成功的模拟鸭子的游戏,游戏中会出现各种鸭子 ,一边游泳戏水,一边呱呱叫,此系统的内部设计使用了标准的OO设计,设计了一个鸭子的超类(superclass),并让各种鸭子继承此超类

所有的鸭子都会呱呱叫和游泳,所以让超类负责处理这部分的实现代码

每个鸭子的子类型负责实现自己的display()行为在屏幕上显示其外观

现在主管们决定,需要模拟程序需要会飞的鸭子,在这个时候,Joe告诉主管们,他一个星期就能搞定,这有什么困难?Joe 需要在duck类中假如fly()方法,然后所有鸭子就会飞了,他很高兴的去交差

但是可怕的事情发生了,老板在看的时候发现很多的橡皮鸭子在屏幕上飞来飞去,于是通知他准备另寻工作了。

原来,Joe忽略了一件事,并非Duck所有的子类都会飞,Joe在Duck类上加上新的行为,会使某些并不适合该行为的子类也具有该行为。现在可好,程序中有一个无生命会飞的东西。

对代码所做的局部修改,影响层面可不只是局面(比如会飞的橡皮鸭)

他体会到了一件事,当涉及“维护”时,为了复用目的而使用继承的结局并不完美。

public class RubberDuck extends Duck {@Overridevoid display() {System.out.println("我是橡皮鸭");}@Overridepublic void quack() {System.out.println("修改为吱吱叫");}
}复制代码

当前结构:

Joe突然想到,只要像quack()方法一样,把fly()方法覆盖掉就可以了。

@Override
void fly() {// 什么事都不做
}复制代码

可是又衍生出另一个问题,那我以后要加个木头鸭呢,不会飞也不会叫。

public class DecoyDuck extends Duck {@Overridevoid display() {System.out.println("我是木头鸭");}@Overridepublic void quack() {// 什么事都不做}@Overridevoid fly() {// 什么事都不做}
}复制代码

每次有新的鸭子的子类出现,他就要被迫检查并可能需要覆盖掉fly()和quack() ,这简直是无穷无尽的噩梦。。。

所以,他需要一个更清晰的方法,让某些鸭子类型可飞或可叫

有一个想法: 把fly()从超类中取出来,这么一来,只有会飞的鸭子实现flyable接口,同样的方法可以用在quack(),设计一个quackable接口

public interface Quackable {void quack();
}复制代码
public interface Flyable {void fly();
}
复制代码

实现为继承  虚线为实现

大家觉得这个设计如何?

可想而知 ,这是一个超笨的方法,这样一来重复的代码会变多,万一需要修改48个duck的子类的fly行为,你又需要全部都修改了。

我们知道继承并不是适当的 解决方式,虽然flyable 和quackable可以解决一部分问题,但是造成了代码无法复用,这只能说是从一个噩梦跳到另一个噩梦了。

接着往下看

软件开发的一个不变真理:不管软件当初设计得多好,一段时间总是需要成长与改变,否则软件就会死亡。

我们把问题归零,现在我们知道继承并不能很好的解决我们的问题,flyable接口也是。幸运的是有一个设计原则(不是设计模式) 可以帮我们很好的解决这个问题。

设计原则1:找到应用中可能需要变化的地方,把它们独立出来,不要和那些不需要变化的代码混在一起。(这是我们的第一个设计原则,后面还会有)

换句话说,每次新的需求一来,都会使某方面的代码发生改变,那么你就可以确定,这段代码需要被抽出来。把需要变化的部分抽取出来并封装起来,以便以后可以轻易改变或者扩充这部分,不影响不需要变化的部分。

回到我们的问题,把鸭子的行为fly 和quack从duck类中取出。

设计鸭子的行为,我们希望一切能有弹性,我们应该在鸭子类中包含设定行为的方法,这样就可以在运行时动态的改变绿头鸭的飞行行为。

有了这些目标,接下来看看第二个设计原则:

设计原则2:针对接口变成,而不是针对实现编程

从现在开始,鸭子的行为将被放到分开的类中,此类专门提供某行为接口的实现。这样,鸭子类就不再需要知道行为的实现细节。

我们利用接口代表每个行为,比方说,flyBehavior 和quackBehavior,而行为的每个实现都将实现其中的一个接口。所以这次鸭子类不会实现flyable和quackable接口,反而由我们制造一组其他类专门实现flyBehavior 和quackBehavior,这个就被称为行为类。由行为类而不是duck类来实现行为接口。

public interface FlyBehavior {void fly();
}复制代码
public class FlyCanWay implements FlyBehavior {@Overridepublic void fly() {System.out.println("会飞");}
}复制代码
public class FlyNotWay implements FlyBehavior {@Overridepublic void fly() {System.out.println("不会飞");}
}复制代码
public interface QuackBehavior {void quack();
}复制代码
public class Quack implements QuackBehavior {@Overridepublic void quack() {System.out.println("呱呱叫");}
}复制代码
public class Squeak implements QuackBehavior {@Overridepublic void quack() {System.out.println("吱吱叫");}
}复制代码

这样的设计,可以让飞行和呱呱叫的动作被其他的对象复用,因为这些行为已经与鸭子类无关了,而我们可以新增一些行为,不会影响到既有的行为,也不会影响使用到飞行的鸭子类

现在开始整合鸭子的行为

做法是这样:

首先在duck类中加入两个实例变量,为别为flyBehavior和quackBehavior,每个对象都会动态的设置这些变量以运行时引用正确的行为类型。

我们找两个类似的方法performFly()和performQuack()取代duck中的fly()和quack()。往下看

@Data
public abstract class Duck {/* 鸭子游泳 */public void swim(){}/* 鸭子形状  可能有很多种类的鸭子,所以display方法是抽象的 */abstract void display();FlyBehavior flyBehavior;QuackBehavior quackBehavior;void performFly(){// 每只鸭子都会引用实现FlyBehavior接口的对象flyBehavior.fly();}void performQuack(){quackBehavior.quack();}
}复制代码

现在我们来关心如何设定flyBehavior和quackBehavior变量

public class MallardDuck extends Duck {@Overridevoid display() {System.out.println("我是绿头鸭");}/***  因为继承了duck 所以拥有这两个变量*/public MallardDuck() {quackBehavior = new Quack();flyBehavior = new FlyCanWay();}
}复制代码
绿头鸭使用quack类处理呱呱叫,所以当performQuack被调用时,叫的职责被委托给quack对象,我们就得到真正的呱呱叫。
看懂了吗? 当

MallardDuck实例化时,它的构造器会把继承过来的 quackBehavior实例变量初始化为quack类型的新实例,flyBehavior同理。

测试一下

public static void main(String[] args) {Duck mallardDuck = new MallardDuck();mallardDuck.performFly();mallardDuck.performQuack();
}复制代码

结果

如何让鸭子具有动态行为呢?

利用flyBehavior的setter方法,我们可以随时调用setter改变鸭子的行为。

public void setFlyBehavior(FlyBehavior flyBehavior) {this.flyBehavior = flyBehavior;
}
public void setQuackBehavior(QuackBehavior quackBehavior) {this.quackBehavior = quackBehavior;
}复制代码

加入我们的新鸭子种类

public class ModelDuck extends Duck {@Overridevoid display() {System.out.println("我是一只橡皮鸭");}public ModelDuck() {flyBehavior = new FlyNotWay();quackBehavior = new Squeak();}
}复制代码

此时,我们让飞拥有火箭飞,和普通飞两种,实现flyByhavior接口

public class FlyRocket implements FlyBehavior {@Overridepublic void fly() {System.out.println("火箭飞");}
}复制代码

只使得本来不会飞的模型鸭 变成 火箭飞

public static void main(String[] args) {Duck mallardDuck = new MallardDuck();mallardDuck.performFly();mallardDuck.performQuack();Duck modelDuck = new ModelDuck();modelDuck.performFly();modelDuck.setFlyBehavior(new FlyRocket());modelDuck.performFly();
}复制代码

结果:

会飞

呱呱叫

不会飞

火箭飞

好,我们已经深入研究了鸭子模拟器的设计了。下面是模型图

当你将继承和实现一起组合使用时就衍生另一个设计原则:

设计原则3:多用组合(继承加实现),少用继承

这文到这里就结束了,其实上面的就是第一个设计模式:也就是策略模式!

策略模式:分别封装起来,让他们之间可以互相替换。

总结

其实我这本书还没看完,看了第一次懵懵懂懂,第二次看就会有很大的理解,第三次看慢慢的轮廓就出现在我的脑海中。我写这篇文章的时候应该是我第四次边看边写下来。

接下来会研究继续写,即使没人看哈哈哈

设计模式:简单的鸭子模型(入门)相关推荐

  1. 鸭子模型_如果它看起来像鸭子,嘎嘎像鸭子,但需要电池-您的抽象错误

    鸭子模型 因此,您知道一般的编码方式,了解面向对象的编程,学习过C ++并完成了至少一门软件开发课程(如果您还没有,那么这些文章不适合您). 如果您至少知道一种编程语言,则可以轻松编写软件,但是您的代 ...

  2. ROS探索总结(四)(五)(六)——简单的机器人仿真 创建简单的机器人模型smartcar 使用smartcar进行仿真

    ROS探索总结(四)--简单的机器人仿真 前边我们已经介绍了ROS的基本情况,以及新手入门ROS的初级教程,现在就要真正的使用ROS进入机器人世界了.接下来我们涉及到的很多例程都是<ROS by ...

  3. 设计模式之结构型模型

    设计模式之结构型模型 桥接模式 尽可能不要使用类的继承,而尽可能使用 合成/聚合 描述: 继承方法子类与父类的高依赖性限制了复用和程序的灵活性. 选择不同的接口实现选择不同的业务类型 import o ...

  4. 因果模型一:因果模型入门综述

    因果模型一:因果模型入门综述 一. 为什么要研究因果模型? 二. 因果研究的发展历程 1. C.G. Hempel 1984--因果研究的分水岭 2. 统计相关性模型 3. 虚假原因 三.INUS条件 ...

  5. python设计模式【8】-模型·视图·控制器-复合模式

    UML类图简介 设计模式的分类 面向对象的设计原则 python设计模式[1]-单例模式 python设计模式[2]-工厂模式 python设计模式[3]-门面模式 python设计模式[4]-代理模 ...

  6. 第19章 随机波动率模型入门

    这学期会时不时更新一下伊曼纽尔·德曼(Emanuel Derman) 教授与迈克尔B.米勒(Michael B. Miller)的<The Volatility Smile>这本书,本意是 ...

  7. 通过Gazebo建立简单室内环境模型并用launch文件打开

    本文叙述如何直接使用Gazebo创建简单室内环境模型,并能够使用launch文件打开 一.利用Gazebo建立好环境模型 1. 打开Gazabo的编辑界面 运行如下命令后按Ctrl+B,进入到编辑界面 ...

  8. 和我一起打造个简单搜索之SpringDataElasticSearch入门

    网上大多通过 java 操作 es 使用的都是 TransportClient,而介绍使用 SpringDataElasticSearch 的文章相对比较少,笔者也是摸索了许久,接下来本文介绍 Spr ...

  9. 初学者指南:使用 Numpy、Keras 和 PyTorch 实现最简单的机器学习模型线性回归

    来源:DeepHub IMBA 本文约5100字,建议阅读10分钟 本文将使用 Python 中最著名的三个模块来实现一个简单的线性回归模型. 机器学习是人工智能的一门子科学,其中计算机和机器通常学会 ...

  10. JavaScript设计模式--简单工厂模式例子---XHR工厂

    JavaScript设计模式--简单工厂模式例子---XHR工厂 第一步,Ajax操作接口(目的是起一个接口检测作用) (1)引入接口文件 //定义一个静态方法来实现接口与实现类的直接检验 //静态方 ...

最新文章

  1. 真正毁掉一个人的,是“打工者心态”
  2. idea可以使用flash框架吗_这个框架厉害了,使用它几分钟就可以编写一个微信插件...
  3. HDU 1241Oil Deposits---(dfs)
  4. MySqlClient访问tinyint字段返回布尔值
  5. Cracking the coding interview--Q1.4
  6. 关于Jakarta EE软件包名称更改的思考
  7. NOIP2001-普及组复赛-第2题-最大公约数和最小公倍数问题
  8. java用一条语句判断一个整数是不是2的整数次方
  9. Chrome安装Octotree插件
  10. Mac 没有声音怎么恢复
  11. [深度学习概念]·主流声学模型对比
  12. 计算机工资表怎么打,Word怎么制作工资条 Word制作工资条教程-电脑教程
  13. 第八届中国信息安全大会在京召开
  14. 【思维导图】Excel转成思维导图
  15. idea 2020.1 连接MySQL数据库的两种方法
  16. 逆幂律模型_逆幂律曲线的基本架构永远不会改变
  17. 计算机毕设网页设计源码——HTML+CSS+JS+Bootstrap在线音乐试听播放网站模板
  18. 渗透学习-靶场篇-WebGoat靶场(JWT攻击)
  19. html用divagt;做个按钮,Diva验证工具使用说明:
  20. PAT 1166 Summit

热门文章

  1. 如何删除下一页分节符_怎么去掉分节符下一页
  2. 美国自动驾驶汽车法规
  3. 信息搜索的基本技能总结
  4. keil编译出现多重定义的问题
  5. c++win32项目 如何显示后再删除一个绘图_50个CAD绘图小技巧,来get成倍提高绘图效率...
  6. app开发(Uniapp开发)之Sass学习
  7. 动画三部曲--属性动画
  8. 以图搜图 图像匹配_基于内容的图像检索(CBIR) ——以图搜图
  9. win10 1903错误应用程序无法正常启动0xc0000135解决
  10. Spring Cloud Stream初窥