设计模式是被前人发现、经过总结形成了一套某一类问题的一般性解决方案。使用模式最好的方式是:把模式装进脑子,然后在设计和已有的应用中,寻找何处可以使用它们。以往是代码复用,现在是经验复用。

从模拟鸭子游戏说起

初始需求:各种鸭子(野鸭MallardDuck、红头鸭RedheadDuck等),一边游泳swim,一边呱呱叫quack,每种鸭子外观都不同display。

初始方案:设计一个鸭子超类,并让各种鸭子继承此超类。

需求变更:让鸭子能够飞fly。

方案1:在Duck类中加上fly()。

结果:所有鸭子都继承fly,但并非所有鸭子都会飞,比如橡皮鸭RubberDuck(继承牵一发动全身)。

方案1_1:对于不能飞的鸭子,在子类中覆盖fly()。

结果:每当有新的鸭子出现,就要被迫检查并可能需要覆盖fly()。同理,对于叫声quack也一样。

方案2:把fly()和quack()从超类中取出,放进Flyable和Quackable接口中,只会飞的鸭子实现Flyable,会叫的实现Quackable。

结果:不会再有会飞的橡皮鸭,但由于接口不具有实现代码,造成代码无法复用(接口无法达到代码复用)。

方案2_1:设计两个接口FlyBehavior和QuackBehavior,还有它们对应的类,负责实现具体行为。

结果:飞行和呱呱叫动作可以被其他的对象复用,因为这些行为已经与鸭子类无关;可以新增一些行为,不会影响到既有的行为类,也不会影响使用飞行行为的鸭子类(有了继承的复用好处,没了继承所带来的包袱)。

完善方案2_1:在Duck类中加入两个变量“flyBehavior”和“quackBehavior”,声明为接口类型,用performFly和performQuack分别取代fly和quack;同时加入两个新方法setFlyBehavior和setQuackBehavior,用来随时改变鸭子的行为。

测试Duck的代码

Duck类(Duck.java)

public abstract class Duck {
//所有鸭子子类都继承它
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck(){
}
public abstract void display(); public void perfmormFly(){
//委托给行为类
flyBehavior.fly();
}
public void performQuack(){
//委托给行为类
quackBehavior.quack();
}
public void setFlyBehavior(FlyBehavior fb){
flyBehavior=fb;
}
public void setQuackBehavior(QuackBehavior cb){
quackBehavior=cb;
}
public void swim(){
System.out.println("All ducks float,even decoys!!");
}
}

MallardDuck类和ModelDuck类(MallardDuck.java与ModelDuck)

public class MallardDuck extends Duck{
public MallardDuck(){
quackBehavior=new Quack();
flyBehavior=new FlyWithSwings();
}
public void display(){
System.out.println("I'm a real Mallard Duck");
}
} public class ModelDuck extends Duck{
public ModelDuck(){
flyBehavior=new FlyNoWay();
quackBehavior=new MuteQuack();
}
public void display(){
System.out.println("I'm a model duck!!");
}
}

FlyBehavior接口(FlyBehavior.java)与三个行为实现类(FlyWithWings.java、FlyNoWay.java和新增的FlyRocketPowered.java)

public interface FlyBehavior {
public void fly();
} public class FlyWithSwings implements FlyBehavior{
public void fly(){
System.out.println("I'm flying!!");
}
} public class FlyNoWay implements FlyBehavior{
public void fly(){
System.out.println("I can't fly!!");
}
} public class FlyRocketPowered implements FlyBehavior{
public void fly(){
System.out.println("I'm flying with a rocket!!");
}
}

QuackBehavior接口(QuackBehavior.java)及其三个实现类(Quack.java、MuteQuack.java、Squeak.java)

 public interface QuackBehavior {
public void quack();
} public class Quack implements QuackBehavior{
public void quack(){
System.out.println("Quack!!");
}
} public class MuteQuack implements QuackBehavior{
public void quack(){
System.out.println("Silence!!");
}
} public class Squeak implements QuackBehavior{
public void quack(){
System.out.println("Squeak");
}
}

测试类(MiniDuckSimulator.java)

public class MiniDuckSimulator {
public static void main(String[] args) {
// TODO Auto-generated method stub
Duck mallard=new MallardDuck();
mallard.perfmormFly();
mallard.performQuack();
Duck model=new ModelDuck();
model.perfmormFly();
model.performQuack();
model.setFlyBehavior(new FlyRocketPowered());
model.perfmormFly();
}
}
/*
*
* 运行结果:
* Quack!!
* I can't fly!!
* Silence!!
* I'm flying with a rocket!!
*/

这就是策略模式

策略模式(Strategy Pattern)定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。封闭可以互换的行为,并使用委托来决定要使用哪一个。

策略模式结构

Strategy:可由接口或抽象类来实现;定义所有支持的算法的公共接口;Context使用这个接口来调用某ConcreteStrategy定义的算法。

ConcreteStrategy:具体的策略实现,也就是具体的算法实现。

Context:用一个ConcreteStrategy对象来配置;维护一个对Strategy对象的引用;可定义一个接口来让Strategy访问它的数据。

策略模式示例代码

/**
* 策略,定义算法的接口
*/
public interface Strategy {
// 某个算法的接口,可以有传入参数,也可以有返回值
public void algorithmInterface();
} /**
* 实现具体的算法
*/
public class ConcreteStrategyA implements Strategy {
public void algorithmInterface() {
//具体的算法实现
}
}
public class ConcreteStrategyB implements Strategy {
public void algorithmInterface() {
//具体的算法实现
}
}
public class ConcreteStrategyC implements Strategy {
public void algorithmInterface() {
//具体的算法实现
}
} /**
* 上下文对象,通常会持有一个具体的策略对象
*/
public class Context {
//持有一个具体的策略对象
private Strategy strategy; // 构造方法,传入一个具体的策略对象
//@param aStrategy 具体的策略对象
public Context(Strategy aStrategy) {
this.strategy = aStrategy;
} // 上下文对客户端提供的操作接口,可以有参数和返回值
public void contextInterface() {
//通常会转调具体的策略对象进行算法运算
strategy.algorithmInterface();
}
}

关于策略模式, 最后推荐一篇博文:http://www.uml.org.cn/sjms/201009092.asp

设计模式入门——Head First的更多相关文章

  1. 基于java的设计模式入门(1)——为什么要学习设计模式

    大年初一,楼主在这里给大家拜年,祝大家码上升职加薪,码上有对象结婚,码上有车有房,幸福安康. 过完年,回学校注册报道之后,大概就要回深圳到公司开始实习了.提高自己,无非就有两种方式,一是看书学习,二是 ...

  2. 设计模式入门,策略模式,c++代码实现

    // test01.cpp : Defines the entry point for the console application.////第一章,设计模式入门,策略模式#include &quo ...

  3. 写给Python初学者的设计模式入门

    有没有想过设计模式到底是什么?通过本文可以看到设计模式为什么这么重要,通过几个Python的示例展示为什么需要设计模式,以及如何使用. 设计模式是什么? 设计模式是经过总结.优化的,对我们经常会碰到的 ...

  4. HeadFirst学习笔记-1. 设计模式入门

    1.概念 在开始学习前,我们先了解一些概念,方便我们接下来的学习. OO基础 抽象 继承 多态 封装 OO原则 封装变化 多用组合,少用继承 针对接口编程,不针对实现编程 设计模式 设计模式(Desi ...

  5. 设计模式入门,适配器模式,c++代码实现

    // test07.cpp : Defines the entry point for the console application.// #include "stdafx.h" ...

  6. 设计模式入门,命令模式,c++代码实现

    // test06.cpp : Defines the entry point for the console application.////设计模式第5章 命令模式#include "s ...

  7. 设计模式入门,单件模式,c++代码实现

    // test05.cpp : Defines the entry point for the console application.// #include "stdafx.h" ...

  8. 设计模式入门,工厂模式,c++代码实现

    // test04.cpp : Defines the entry point for the console application.////设计模式第4章 工厂模式#include "s ...

  9. 设计模式入门,装饰着模式,c++代码实现

    // test03.cpp : Defines the entry point for the console application.////设计模式第3章 装饰者模式#include " ...

随机推荐

  1. [UE4]移动相机,使用Arrow组件来标记移动位置

    一.创建一个Arrow组件来标记要移动的位置(Arrow的用法之一就是用来标注坐标). 二.使用TimeLine时间轴结合插值Lerp来移动相机

  2. Mongodb集群搭建之 Sharding+ Replica Sets集群架构(2)

    参考http://blog.51cto.com/kaliarch/2047358 一.概述 1.1 背景 为解决mongodb在replica set每个从节点上面的数据库均是对数据库的全量拷贝,从节 ...

  3. CRM4.0 上传附件大小限制

    问题背景: 上传附件大小默认为 5M 可以通过以下设置修改其大小 : "设置"--〉“管理”--〉“系统设置”--〉“电子邮件”--〉“设置附件文件的大小限制”

  4. 数组.html

    <script > var arr1 = [1, 2, 3, 4, 5, 6 ]; 赋值 var arr2 =Array(1,2,3,4,5,6); var arr3 = new Arra ...

  5. Don't afraid point

    int p; int *p; int p[3]; int *p[3];分析方式:首先从P开始分析,先与[]结合因为其优先级比*高,所以p是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与 ...

  6. 动态添加布局、动态添加View、LinearLayout动态添加View;

    LinearLayout提供了几个方法,用作动态添加View特别好用: 可以添加View.删除View.删除指定位置View.删除全部View: 看代码: public class MainActiv ...

  7. nvm+nodejs+npm

    安装nvm 1.下载nvm包,地址:https://github.com/coreybutler/nvm-windows/releases选择nvm-setup.zip下载, 解压,双击安装即可 2. ...

  8. 查看计算机CPU、内存使用情况

    Shift + Ctrl + Esc,打开Windows任务管理器,点击性能,如图: 可以清楚的看到整台机子的CPU.内存使用情况,其中CPU使用记录下有8个小窗口,因为博主的CPU是8核的,讲讲CP ...

  9. day19常用模块2

    常用模块21 shelve模块  也是一种序列化方式    使用方法        1.open     sl = shelve.open("shelvetest.txt")   ...

  10. 编码,基本数据类型,str索引和切片,for循环

    1. 编码 1. 最早的计算机编码是ASCII. 美国人创建的. 包含了英文字母(大写字母, 小写字母). 数字, 标点等特殊字符!@#$% 128个码位 2**7 在此基础上加了一位 2**8 8位 ...