有些人已经解决你的问题了

什么是设计模式?我们为什么要使用设计模式?怎样使用?按照书上的说法和我自己的理解,我认为是这样的:我们遇到的问题其他开发人员也遇到过,他们利用他们的智慧和经验将问题解决了,把他们解决问题的方法提炼总结出来使其能解决其他同类问题。使用设计模式是为了更方便快捷的解决问题。把模式装进脑子里,然后在你的设计和已有的应用中,寻找何处可以使用这些模式,以往是代码复用,现在是经验复用。

先把书上的例子过一遍,简单的鸭子模拟应用

Joe所在的公司决定开发一款模拟鸭子的应用。系统中有各种鸭子,鸭子可以游泳,可以呱呱叫,各种鸭子有不同的外观。此系统建立了一个鸭子的基类,其中有游泳的方法swim,有呱呱叫的方法quack,有显示鸭子外貌的方法display。每种鸭子的外貌不同,必须在其子类中重写display方法。就像这样:

由于竞争加剧,公司决定搞定不一样的东西:“嘿,Joe,我想鸭子应该能飞!”,“嗯, 这个听起来很容易”,在Duck中加一个fly方法就行了。过了几天,公司开会,“Joe,怎么会有一只橡皮鸭(RubberDuck,不会飞,吱吱叫)在屏幕里面飞来飞去?”,好吧,这是Joe疏忽了,只有真正的鸭子能飞,橡皮鸭会叫,会游泳但是不会飞,马上修复(覆盖RubberDuck中的fly方法, 让它什么也不做)。但是如果我需要一只诱饵鸭(DecoyDuck,不会飞也不会叫)呢,也在子类中重写quack和fly方法?Joe还收到通知,此系统还会不定时更新,至于怎么更新还没有想到,于是Joe意识到继承不是一个好方法,因为每添加一个鸭子的子类,他就要被迫检查该子类的quack和fly方法并可能需要重写他们,如果直接修改父类中的方法,但有些子类并不想修改,那么这些子类就都要重写这些方法。

继承所采用的代码

Duck

public abstract class Duck {
public void quack(){
System.out.println("呱呱叫");
} public void swim(){
System.out.println("游泳");
} //每个鸭子的外观不同, 在子类中实现
public abstract void display(); //鸭子飞行的方法
public void fly(){
System.out.println("飞行");
}
}

MallardDuck

/**
* 外观是绿色的鸭子
*/
public class MallardDuck extends Duck { @Override
public void display() {
System.out.println("绿头鸭");
} }

RubberDuck

/**
* 橡皮鸭
* 橡皮鸭不会呱呱叫(quack), 而是吱吱叫(squeak)
*/
public class RubberDuck extends Duck { @Override
public void display() {
System.out.println("可爱的黄色橡皮鸭");
} //橡皮鸭不会呱呱叫(quack), 而是吱吱叫(squeak)
@Override
public void quack() {
System.out.println("橡皮鸭吱吱叫");
} //橡皮鸭不会飞
@Override
public void fly() {
}
}

DecoyDuck

/**
* 诱饵鸭, 不会飞也不会叫
*/
public class DecoyDuck extends Duck { @Override
public void display() {
System.out.println("诱饵鸭");
} @Override
public void quack() {
System.out.println("什么都不会做, 不会叫");
} @Override
public void fly() {
System.out.println("什么都不做, 不会飞");
}
}

采用接口呢

将行为抽离出来,比如将fly方法抽离出来放到Flyable接口中,只有会飞的Duck的子类才实现该接口,同样的也可以将quack方法抽离到Quackable接口中。就像这样:

这样解决了一部分问题,至少橡皮鸭不会到处飞了。但是你有没有想过另外的一个问题,就是这样代码无法复用,因为接口中的方法只能是抽象的,这样导致在每个子类中都需要重写方法。这样无疑是从一个噩梦跳入了另一个噩梦。

变化与不变分离

好吧,在软件开发上,有什么是你深信不疑的,那就是——change,不变的是变化。

虽然使用接口也不妥,但思想是值得借鉴的: 把变化的和不变的离,那哪些是变化的呢?“真正的”鸭子会游泳swim,会呱呱叫quack,会飞fly,而橡皮鸭会游泳swim,会吱吱叫squeak,不会飞。所以说叫可以是呱呱叫,可以是吱吱叫,也可以不叫,飞可以沿直线飞,可以沿曲线飞,也可以不会飞,所以叫和飞是变化的。游泳都是在水面上无规则的游泳,可以看作是不变的,当然,你要认为游泳有多种方式,也可以认为它是变化的,我在这里把游泳认为是不变的,所以说判断变化的和不变的要根据实际情况来定。

软件设计原则: 变化与不变分离

找出应用中可能会变化的部分,把它们独立出来,不要把他们和那些不变的混在一起。即把应用中可能会变化的抽离出来并封装起来。

继续设计

基于变化和不变分离的原则,我们可以把变化的(飞和叫)抽离出来并封装起来。比如将飞行的行为抽离出来设计成一个接口FlyBehavior,将叫的行为设计成接口QuackBehavior,然后具体的行为实现接口并重写方法,如:

针对接口编程

我们把鸭子的行为从鸭子类Duck中分离出来,和以往不同,以往的做法是:行为是从Duck中的具体实现继承过来,或是实现接口重写方法而来,这两种方式都依赖实现,我们被实现绑得死死的。在我们新的设计中,我们把行为分离出来,所以行为不会绑死在鸭子的子类中,换句话说现实行为的代码位于特定类QuackBehavior和FlyBehavior中,可以在运行时动态的改变行为。这就是针对接口编程的一种体现,针对接口编程指的是针对超类型编程,不一定是interface,可以是abstract class。

软件设计原则:针对接口编程

针对接口编程,而不是针对实现编程

整合实现代码

首先是两个行为接口QuackBehavior和FlyBehavior

QuackBehavior

public interface QuackBehavior {
void quack();
}

FlyBehavior

/**
* 飞行行为接口
*/
public interface FlyBehavior {
void fly();
}

QuackBehavior具体行为实现,呱呱叫Quack,吱吱叫Squeak,不会叫MuteQuack

Quack

/**
* 呱呱叫
*/
public class Quack implements QuackBehavior {
@Override
public void quack() {
System.out.println("呱呱叫");
}
}

Squeak

/**
* 吱吱叫
*/
public class Squeak implements QuackBehavior {
@Override
public void quack() {
System.out.println("吱吱叫");
}
}

MuteQuack

/**
* 什么都不做, 不会叫
*/
public class MuteQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("什么都不做, 不会叫");
}
}

FlyBehavior的具体实现,用翅膀飞FlyWithWings,不会飞FlyNoWay

FlyWithWings

/**
* 用翅膀飞
*/
public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("用翅膀飞行");
}
}

FlyNoWay

/**
* 不会飞
*/
public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("什么都不做, 不能飞");
}
}

然后是基类Duck,将行为接口当做实例变量放入Duck中,需要在运行时动态的改变行为,可以提供setter方法

Duck

public abstract class Duck {
//针对接口编程的体现
private FlyBehavior flyBehavior;
private QuackBehavior quackBehavior; //设置一个默认的行为
public Duck() {
flyBehavior = new FlyWithWings();
quackBehavior = new Quack();
} public void swim(){
System.out.println("游泳");
} //每个鸭子的外观不同, 在子类中实现
public abstract void display(); //执行呱呱叫的方法
public void performQuack(){
quackBehavior.quack();
} //执行飞行的方法
public void performFly(){
flyBehavior.fly();
} public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
} public void setQuackBehavior(QuackBehavior quackBehavior) {
this.quackBehavior = quackBehavior;
}
}

注意,抽象类也是有构造方法的,不过不能创建对象,抽象类的构造方法是用来初始化变量的

两个子类MallardDuck和RubberDuck

MallardDuck

/**
* 外观是绿色的鸭子
*/
public class MallardDuck extends Duck { @Override
public void display() {
System.out.println("绿头鸭");
} @Test
public void test1() throws Exception {
MallardDuck mallardDuck = new MallardDuck();
mallardDuck.performQuack();
mallardDuck.performFly();
display();
}
}

RubberDuck

/**
* 橡皮鸭
* 橡皮鸭不会呱呱叫(quack), 而是吱吱叫(squeak)
*/
public class RubberDuck extends Duck {
@Override
public void display() {
System.out.println("可爱的黄色橡皮鸭");
} @Test
public void test1() throws Exception {
Duck rubberDuck = new RubberDuck();
//运行时动态改变行为
rubberDuck.setFlyBehavior(new FlyNoWay());
rubberDuck.setQuackBehavior(new Squeak());
rubberDuck.performFly();
rubberDuck.performQuack();
rubberDuck.display();
}
}

封装行为

好了,我们来看看整体的格局:飞行行为实现了FlyBehavior接口,呱呱叫行为实现了QuackBehavior接口,也请注意,我们描述事务的方式也有所改变,我们把行为看成一组算法,飞行行为是算法,呱呱叫行为也是算法,就像这样:

我们把QuackBehavior和FlyBehavior类型的变量放到了Duck中,这其实就用到了组合。用组合创建的系统更具有弹性,不会如继承一般一处改可能需要多处改

软件设计原则:

多用组合,少用继承

策略模式

好了,到这里我们终于学到了第一个模式:策略模式(strategy pattern),介绍一下策略模式的概念

定义了算法族,并分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户

读headFirst设计模式 - 策略模式的更多相关文章

  1. 读headFirst设计模式 - 工厂模式

    每次写博客都不知道要怎么引入要写的主题,挺头疼的一件事.今天就直接开门见山,今天要学的就是工厂模式,工厂就是批量生产制造东西的地方.在这里,工厂就是批量生产对象的地方. 学习书上的例子 假如你现在有一 ...

  2. 15. 星际争霸之php设计模式--策略模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  3. [.net 面向对象程序设计深入](24)实战设计模式——策略模式(行为型)

    [.net 面向对象程序设计深入](24)实战设计模式——策略模式(行为型) 1,策略模式定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模式让算法独立于使用它 ...

  4. linkin大话设计模式--策略模式

    linkin大话设计模式--策略模式 Strategy [ˈstrætədʒi]  策略 策略模式用于封装系列的算法,这些算法通常被封装在一个称为Context的类中,客户端程序可以自由的选择任何一种 ...

  5. [.net 面向对象程序设计深入](26)实战设计模式——策略模式 Strategy (行为型)

    [.net 面向对象程序设计深入](26)实战设计模式——策略模式 Strategy (行为型) 1,策略模式定义 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换.策略模 ...

  6. 读headFirst设计模式 - 装饰者模式

    继承可以在复用父类代码的情况下扩展父类的功能,但同时继承增加了对象之间的耦合度,所以要慎用继承.那么有没有既能扩展父类的功能,又能使对象间解耦的方法呢?答案是肯定的,这就是我们今天要学习的装饰者模式. ...

  7. JAVA 设计模式 策略模式

    用途 Title 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户. 策略模式是一种行为型模式. 结构

  8. 设计模式-策略模式(Strategy Model)

    1.概述     在开发过程中常常会遇到类似问题,实现一个功能的时候往往有多种算法/方法(策略),我们可以根据环境的不同来使用不同的算法或策略来实现这一功能.     如在人物比较排序的实现中,我们有 ...

  9. java设计模式 策略模式Strategy

    本章讲述java设计模式中,策略模式相关的知识点. 1.策略模式定义 策略模式,又叫算法簇模式,就是定义了不同的算法族,并且之间可以互相替换,此模式让算法的变化独立于使用算法的客户.策略模式属于对象的 ...

随机推荐

  1. 一个可以直接使用的MsgBox基于form居中API

    可直接复制DialogBox项目(文件夹)到需要的项目中然后直接引用和using CodeProject.Dialog 已修正原作者代码错误的地方,可直接使用,VS2010测试成功 具体可以参考案例T ...

  2. MvcPager分页控件以适用Bootstrap

    随笔- 9  文章- 0  评论- 33  修改MvcPager分页控件以适用Bootstrap 效果(含英文版,可下载)   软件开发分页效果必不可少,对于Asp.Net MVC 而言,MvcPag ...

  3. Gimp教程:简约手机图标风格

    效果:       在一个国外博客上翻到的图标制作教程,效果类似于Cowon J3的默认图标风格. 制作过程很简单,只需两三步,不多说了,上步骤 Step1.新建50×50的黑色背景 Step2.新建 ...

  4. AjaxPro使用说明文档

    ajaxpro下载地址 AjaxPro使用说明     1 目录     2 修改历史纪录     3 1.什么是Ajax     4 2.为什么使用Ajax     4 3.Ajax应用场景     ...

  5. JBoss7官方下载最新版本

    JBoss是全世界开发人员共同努力的成果.一个基于J2EE的开放源码的应用server. 由于JBoss代码遵循LGPL许可,能够在不论什么商业应用中免费使用它.而不用支付费用. 2006年,Jbos ...

  6. 功能和形式的反思sql声明 一个

    日前必须使用sql语句来查询数据库 但每次你不想写一个数据库中读取所以查了下反射 我想用反映一个实体的所有属性,然后,基于属性的查询和分配值 首先,须要一个实体类才干反射出数据库相应的字段, 可是開始 ...

  7. mysql基础之基本数据类型

    原文:mysql基础之基本数据类型 列类型学习 mysql三大列类型 整型 Tinyint/ smallint/ mediumint/int/ bigint(M) unsigned zerofill ...

  8. w5cValidator【AngularJS】 2.0 版本发布

    w5cValidator 插件基于angular原有的表单验证,在原有的基础上扩展了一些错误提示的功能,让大家不用在每个表单上写一些提示信息的模板,专心的去实现业务逻辑. 代码地址:https://g ...

  9. C#中几个经常犯的错误总汇

    在我们平常编程中,时间久了有时候会形成一种习惯性的思维方式,形成固有的编程风格,但是有些地方是需要斟酌的,即使是一个很小的错误也可能会导致昂贵的代价,要学会善于总结,从错误中汲取教训,尽量不再犯同样错 ...

  10. .NET PageAdmin CMS

    .NET PageAdmin CMS 完全破解步骤(非简单去版权) 其实当初我的目的是很纯洁的,只是想找一个简单的网站生成模板,由于对.net更熟悉一点,就去搜索了.net框架的CMS,看它的介绍挺强 ...