设计模式一 前言与策略模式(strategy)

6 篇文章 0 订阅
6 篇文章 0 订阅

本文为学习记录,编写blog作为复习也方便以后查阅

设计模式在很多同学的脑海中是一个比较抽象的概念,有的童鞋认为学会了设计模式,是系统架构师不可缺少的必备知识,有得童鞋认为设计模式是成为大牛的必备条件,而更多的童鞋认为设计模式是自己的逼格提升的必要技能,对此,我只能呵呵咯,因为每个人的想法不径相同,不能强人所难。不过既然学习设计模式,那么大家的终点肯定是一致的~好吧···开头就扯了这么多废话。

切正题:

  1. 设计模式是什么?

    • 模式被认为是历经验证的OO设计经验
    • 模式不是代码,而是针对设计问题的通用解决方案(说白了就是一种 duangduang 的思维方式)
  2. 我们为什么需要学习设计模式?

    • 如大家所知,我们现在开发程序大多使用别人设计好的库与框架。我们讨论框架,利用他们的API写出我们需要的程序。享受别人代码所带来的duang~ 但是······这些设计duang~duang的框架无法帮助我们将应用组织成为容易了解、容易维护、具有弹性的架构。所以需要设计模式。
    • 有些童鞋可能会说:我OO基础 ok,逻辑思维 very good,设计良好的OO系统so easy。但是这些都不足以让你设计出良好的OO系统,良好的OO设计必须兼备复用、可扩充、可维护三个特性,而模式可以做到这些。
  3. 我们如何使用设计模式?

    设计模式不会直接进入你的代码中,而是 先进入你的“大脑” 。一旦你现在脑海中装入了许多关于模式的知识,就能够开始在新设计中采用他们,并当你的旧代码变得稀烂一团的热干面的时候,可以利用他们重构旧代码。

  4. 库和框架是设计模式么?

    库和框架提供了我们某些特定的实现,让我们的代码可以轻易地引用,但是这并不是设计模式,有时候,库和框架本身用到了设计模式,这样很好,因为一旦我们了解了设计模式,那么我们会更加容易了解这些库和框架的内部构造。简而言之,了解了设计模式,会方便我们去读懂大神们写出来的框架源码

其实学习设计模式还能增加沟通能力,众所周知:程序员的沟通能力直接影响到我们在职场中的发展。假如你的同事这时候来问你某一个模块是如何设计的?而你这样回答他:“我建立了这个广播类。它能够追踪所有的倾听对象,而且任何时候只要有新的数据进来,就会通知每个倾听者。最吊的是。倾听者可以随时加入此广播,甚至可以随时退出。这样的设计方式相当动态和松耦合…”。扯了这么多,比唐僧还沙悟净。如果我们都够了解模式,你只需要说:“观察者模式”。好的~万事大吉,从此世界清静了。

记录第一个模式:策略模式

利用一个实际应用场景来去解析这个模式,应该会更加容易理解:RPG游戏大家都玩过,如果我们设计一个简单RPG游戏,游戏中有角色,可以显示自己的名称,可以使用武器。好的~,我们根据上面描述再使用自己的OO思想很快设计了一个角色父类Character。并让各种职业继承此父类。

这是简单的UML图:
这里写图片描述

因为我们每个角色外观都是不一样的,所以父类为抽象类,添加了抽象方法display()。子类女王和女巫负责实现自己的display()行为在屏幕上显示其外观。
这样设计完成了需求,并使代码能够重用,但是显然我们的老板当然不会让我们这么轻易拿到项目奖金,为了让该游戏更有特色亮点,主管们确定,此游戏需要添加“生育”的功能把其它RPG游戏比下去。这个时候,是我们OO程序员大展身手的时候了。
于是有了以下设计:
这里写图片描述

但是可怕的问题发生了:主管愤怒的找到你:我昨天在产品发布会议上看到了国王生孩子的功能。你这是在逗我么?你可能要去51job,智联招聘网站逛逛了!
这里我们犯了一个大家都会犯的错误:对代码的局部修改可能影响到全局。也许我们会想到Override。我可以把国王king类中的born()方法覆盖掉就像覆盖useWeapon一样(King使用的是单手剑,Queen使用的是魔法权杖)可是,如果以后加入狼人职业呢?(狼人生育小狼崽,但不会使用武器)。
我们认识到继承已经无法解决我们现在遇到的bug了,因为刚刚得到的最新通知,希望希望每六个月更新产品,增添一种角色,我们知道每当有新的角色出现,我们就要被迫检查并可能需要覆盖born()和useWeapon()·····这简直就是一种无穷无尽的酸爽~!!!

那我们使用接口吧:把useWeapon()从父类中取出来,放进一个useWeaponable接口中。这么一来,只有会使用武器的角色才实现此接口,同样的方式,也可以提取出一个bornable()接口,因为不是所有的角色都会生育,那么现在的设计如下:
这里写图片描述

经过如此修改,似乎修改了上面所有的bug。不过UseWeaponable和Bornable接口一开始似挺不错的,解决了问题,但是Java接口不具有实现代码,所以每次我们实现一种接口,总是要去实现接口中相关方法代码的编写,无疑是另外一种无穷无尽的酸爽~

既然无论如何我们无法完美解决这个问题,那么我们从新来考虑这个问题:

  • 先提出一个设计原则:找出应用中可能需要变化之处,把他们独立起来,不要和那些不需要变化的代码混在一起。换句话说,如果每次新的需求一来,都会使某处代码发生变化,那么你就可以确定,这部分代码需要被抽出来,和其它稳定的代码有所区分。根据这个设计原则我们再去考虑这个问题,现在我们很明显的知晓:角色的useWeapon()行为和born()行为会随着角色的不同而不同。为了要把这两个行为从Character父类中分开,我们将他们从Character中取出来,建立一组新类来代表每个行为。

  • 接着提出第二个设计原则:针对接口编程,而不是针对实现编程 我们利用接口代表每个行为,比方说UseWeaponBehavior 与BornBehavior,而行为的每个实现都将实现其中的一个接口。所以这次我们制造一组“行为”类,由行为类而不是角色子类去实现行为接口。

一 、在我们新设计中,角色的子类将使用接口(UseWeaponBehavior 与BornBehavior)所表示的行为。所以实际的实现不会被绑死在角色类中(松耦合),关于行为接口的设计如下:

这里写图片描述


/**
 * 使用武器行为接口
 */
public interface UseWeaponBehavior {

    public void useWeapon();

}

public class UseSwords implements UseWeaponBehavior {

    @Override
    public void useWeapon() {
        System.out.println("使用单手剑");
    }

}

public class UseWand implements UseWeaponBehavior {

    @Override
    public void useWeapon() {
        System.out.println("使用魔法权杖");
    }

}
/**
 * 生育行为接口
 */
public interface BornBehavior {

    public void born();

}


public class BornBaby implements BornBehavior {

    @Override
    public void born() {
        System.out.println("生了个可爱孩子");
    }

}

public class BornWolf implements BornBehavior {

    @Override
    public void born() {
        System.out.println("生了个小狼崽");
    }

}
二、这样设计可以让使用武器和生育的行为被其他对象复用,因为这些行为已经完全跟Character角色类无关了。同时我们可以新增一些行为,不会影响到既有的行为类!现在我们设计好了一套行为类(一族算法),那接下来关键在于,角色现在会将使用武器或者生育的行为“委托”别人处理,而不再使用定义在Character角色类中的useWeapon()方法和born()方法:

这里写图片描述

  • 行为变量被声明为行为接口类型,而这些行为变量在运行时持有特定的行为引用(典型的多态思想)
  • performBorn()和performUseWeapon()将取代born()和useWeapon()方法。
  • 我们不在乎useWeaponBehavior接口和bornBehavior接口的对象到底是什么,我们只关心该对象知道如何进行该行为动作就行了。
public abstract class Character {

    private UseWeaponBehavior useWeaponBehavior;

    private BornBehavior bornBehavior;

    /**
     * 动态设置使用武器行为变量的方法
     */
    public void setUseWeaponBehavior(UseWeaponBehavior useWeaponBehavior) {
        this.useWeaponBehavior = useWeaponBehavior;
    }

    /**
     * 动态设置生育行为变量的方法
     */
    public void setBornBehavior(BornBehavior bornBehavior) {
        this.bornBehavior = bornBehavior;
    }

    public abstract void display();

    /**
     * 角色对象不会亲自处理使用武器的行为,而是委托给useWeaponBehavior引用的对象
     */
    public void performUseWeapon(){
        if(useWeaponBehavior != null){
            useWeaponBehavior.useWeapon();
        }else{
            System.out.println("不会使用武器");
        }

    }

    /**
     * 角色对象不会亲自处理生育的行为,而是委托给bornBehavior引用的对象
     */
    public void performBorn(){
        if(bornBehavior != null){
            bornBehavior.born();
        }else{
            System.out.println("不会生育");
        }

    }
}
三、最后我们来实现一个具体的角色类,并为该角色类设定useWeaponBehavior与bornBehavior的实例变量
public class King extends Character {

    public King() {
        setUseWeaponBehavior(new UseSwords());//国王使用单手剑
        setBornBehavior(null);//国王不会生育
        display();
    }

    @Override
    public void display() {
        System.out.println("我是国王");
    }

}

public class Queen extends Character {

    public Queen() {
        setUseWeaponBehavior(new UseWand());//女王使用魔法权杖
        setBornBehavior(new BornBaby());//女王可以生育小孩
        display();
    }

    @Override
    public void display() {
        System.out.println("我是女王");
    }

}

public class Werewolf extends Character {

    public Werewolf() {
        setUseWeaponBehavior(null);//狼人不会使用武器
        setBornBehavior(new BornWolf());//狼人生育小狼崽
        display();
    }

    @Override
    public void display() {
        System.out.println("我是狼人");
    }

}

我们测试一下:

    public static void main(String[] args) {
        Character king = new King();
        king.performUseWeapon();
        king.performBorn();

        Character queen = new Queen();
        queen.performUseWeapon();
        queen.performBorn();

        Character werewolf = new Werewolf();
        werewolf.performUseWeapon();
        werewolf.performBorn();
    }

控制台打印输出为:

我是国王
使用单手剑
不会生育


我是女王
使用魔法权杖
生了个可爱孩子


我是狼人
不会使用武器
生了个小狼崽

上面的Demo就是策略模式完美的体现,现在这个游戏不再担心以后遇到任何变故,我们可以添加女巫职业,不管女巫可以生育猴子还是小女巫,我们只需要创建具体实现类BornMonkey 或者BornWitch实现BornBehavior接口,然后在女巫角色类中调用Characte父类中的setBornBehavior(new BornXXXX())方法就可以让女巫想生什么生什么(女巫可以动态地改变她的生育行为)并且不会影响原有的设计。简单来说:以后我们需要改变Characte的行为,只需要调用Characte中setter方法就可以了。

下面是整体的UML设计:
这里写图片描述

通过上图,我们很清晰直观的看到,每一个角色都有一个UseWeaponBehavior和BornBehavior,好将使用武器行为和生育行为委托给它们处理。当我们将这两个类结合起来使用,这就是一种组合。这种做法和“继承”不同的地方在于,角色的行为不是继承来的,而是和适当的行为对象“组合起来的”。由此提出第三个设计原则:多用组合,少用继承。 使用组合建立系统具有很大弹性,不仅可以将算法族封装成类,更可以在运行时动态改变行为,只要组合的行为对象符合正确的接口标准就ok.

至此,关于策略模式的记录到此为止···

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值