专栏首页彭湖湾的编程世界浅谈设计模式(一):状态模式|外观模式|代理模式

浅谈设计模式(一):状态模式|外观模式|代理模式

前言

计划开一个设计模式的系列,介绍常见的几种设计模式,本文涉及的设计模式包含以下3种

  • 状态模式:state pattern
  • 外观模式:facade pattern
  • 代理模式:proxy pattern

备注:下文适合看过《海贼王》的人阅读,没看过海贼王的观众请在父母陪同下阅读

状态模式:state pattern

在状态模式的设计方案里,一个主类(称为context类),可以在内部状态变化的时候一次性改变它的「所有行为」,而这个「所有行为」会被我们聚合到不同的类(state1,state2,state3)里面去。

这个内部状态我们可以理解为一个可以手动设置的state变量,设置它可以让context内部的state1切换为state2,或者是从state2切换为state3。

这么做,相比起传统的代码逻辑会发生什么变化呢?在传统的代码里,我们可能会在每个方法下,都写一大段if-else的状态判断逻辑里,然后对不同状态分别做处理,这个时候代码非常松散,不利于阅读和扩展,所以我们选择以「状态」为依据, 把这些if-else的每一部分都「聚合」到不同的状态(不同的state类)里面去,然后通过一个主类(context),去统一维护和管理。这样,逻辑上就清晰了很多,也大大降低了维护和扩展的难度。

Example

草帽路飞,是热血漫《海贼王》的主角,像其他许多同类型的作品一样,主角有自己不同层次战斗的状态,进化过程如下所示

  1. 二档:加速血液的流动,大幅提高速度和身体强度,代表大招是「橡胶Jet火箭炮」(拳)和 「橡胶Jet」(踢)
  2. 三档:向橡胶的身体吹入空气,使身体变成巨人,攻击力大增。代表大招是「橡胶巨人火箭炮」(拳)和「橡胶巨人战斧」(踢)
  3. 四档:将武装色霸气和橡胶果实融合,攻击和速度再次强化,代表大招是「橡胶狮子火箭炮」(拳)和「橡胶犀牛榴弹炮」(踢)

下图描述的是主角路飞初次进化为「二档」的历史性时刻

我们发现,主角路飞拥有不同的战斗状态:二档,三档,四档,并且大招的使用是类似的,无非就是用拳头还是用脚踢的问题,但是攻击力和招式上都不同,我们可以根据这个状态的统一性抽象一个state接口出来:

public interface State {
    // 拳打
    public void punch ();
    // 脚踢
    public void kick ();
}

然后创建二档,三档,四档类,并且实现state接口

// 二档
public class SecGearState implements State {
    public void punch () {
        System.out.println("二档:橡胶Jet火箭炮");
    };
    public void kick () {
        System.out.println("二档:橡胶Jet鞭");
    };
}
// 三档
public class ThirdGearState implements State {
    public void punch () {
        System.out.println("三档:橡胶巨人火箭炮");
    };
    public void kick () {
        System.out.println("三档:橡胶巨人战斧");
    };
}
// 四档
public class FourGearState implements State{
    public void punch () {
        System.out.println("四档:橡胶狮子火箭炮");
    };
    public void kick () {
        System.out.println("四档:橡胶犀牛榴弹炮");
    };
}

最后,路飞可能会在战斗中随时切换状态,比如从二档切换到三档,或者从三档切换到四档,所以我们要设置一个Context类去管理,在这个类里面,它有两个功能

  1. 随时切换状态
  2. 代理调用状态类的方法
public class Context {
    State state;
    // 随时切换状态
    public void setState(State state){
        this.state = state;
    }
    // 代理调用状态类的方法
    public void punch () {
        state.punch();
    }
 
    public  void kick () {
        state.kick();
    }
}

测试

public class Test {
    public static void main(String args []) {
        State secGearState = new SecGearState();
        State thirdGearState = new ThirdGearState();
        State fourGearState = new FourGearState();
        Context context = new Context();
        // 路飞进化成二档
        context.setState(secGearState);
        context.punch();
        context.kick();
        System.out.println("----------------");
        // 路飞进化成三档
        context.setState(thirdGearState);
        context.punch();
        context.kick();
        System.out.println("----------------");
        // 路飞进化成四档
        context.setState(fourGearState);
        context.punch();
        context.kick();
    }
}

输出

外观模式:facade pattern

外观模式很简单且容易理解,但理解之后却非常有用。

说白了就是:把不同类的不同接口,统一代理到一个类里面对外输出,使代码具有良好的封装性

Example

咱们还是拿海贼王的一个情境举个例子

比如说,在海贼王367里,草帽海贼团 VS 巨人僵尸奥兹 的时候,索隆,山治,佛兰奇,乌索普和乔巴使用了一招非常精(you)彩(zhi)的技能:合体-大皇帝。

也就是说,合体后的草帽海贼团,在能够使用每个人的绝招的同时,是作为“大皇帝”这个整体对外暴露的

我们使用外观模式去实现的话,代码逻辑如下所示

首先每个成员我们用一个类去表示

// 索隆
public class Zoro {
    public void useSword () {
        System.out.println("三刀流斩击");
    }
}
// 山治
public class Sanj {
    public void kick () {
        System.out.println("恶魔风脚");
    }
}
// 弗兰奇
public class Franky {
    public void openFire () {
        System.out.println("风来炮");
    }
}
// 爱吃棉花糖的乔巴
public class QiaoBa {
    public void cure () {
        System.out.println("回血治疗");
    }
}

然后我们用一个整体的类,去代理上面的每个成员类的逻辑

// 合体后的大皇帝
public class BigKing {
    Franky franky;
    QiaoBa qiaoba;
    Sanj sanj;
    Zoro zoro;
    public BigKing () {
        franky = new Franky();
        qiaoba = new QiaoBa();
        sanj = new Sanj();
        zoro = new Zoro();
    }
    // 索隆类的功能
    public void useSord () {
        zoro.useSword();
    }
    // 山治类的功能
    public void kick () {
        sanj.kick();
    }
    // 佛兰奇类的功能
    public void openFire () {
        franky.openFire();
    }
    // 乔巴类的功能
    public void cure () {
        qiaoba.cure();
    }
}

测试

public class Test {
    public static void main(String args []) {
        BigKing bigking = new BigKing();
        bigking.useSord();
        bigking.kick();
        bigking.cure();
        bigking.openFire();
    }
}

输出

附带一张图,hhhh

代理模式:proxy pattern

使用一个类接管另一个类所有的方法调用,同时能在原来类的方法调用前,加入一些自己的“中间逻辑”。这种方式被称为代理模式。

假设类B 是类A的代理类,那么在调用类B的方法的时候,实际还是通过类B去调用类A的接口,但是现在所有的「控制权」都已经牢牢掌握在类B手里了,代理类B能够很自由的加入一些中间逻辑。

显然,类B和类A起到的功能是相同的,我们可以抽象一个接口,去让原类(A )和代理类(B)去实现

Example

不好意思,这里还是用我熟悉的海贼王打个比方,在七武海-多佛朗明哥刚刚出场的时候,他就用线线果实提供的能力,操控两名海军自相残杀。

如果我们把海军抽象为一个类的话,那么多佛朗明哥就是「海军类」的代理类了,实际上我们发现

  • 实质出手伤人的并不是海军,而是多佛朗明哥,也就是代理类掌握了真正的控制权
  • 直接出手伤人的仍然是海军,也就是代理类仍然调用的是原类的接口

代码如下

1.我们抽象一个海军战士的接口出来

// 海军战士接口
public interface NavyFighter {
    // 使用刀剑
    public void useSword ();
    // 徒手格斗
    public void fight();
    // 使用枪炮
    public void useGuns();
}

2.让海军军官实现这个接口

public class NavyCaptain implements NavyFighter {
    String name = "海军上尉";
    // 海军装备预算不够,不能购买二十一大快刀
    public void useSword() {
      System.out.println(name +"发动了一次普通的斩击");
    }
    // 没有果实能力,只能徒手格斗了
    public void fight() {
        System.out.println(name + "发动了一次普通的拳击");
    }
 
    public void useGuns() {
        System.out.println(name +"打出了一发普通的海楼石子弹");
    }
}

3.让多佛朗明哥也实现这个接口

public class Doflamingo implements NavyFighter {
    NavyFighter navyFighter;
    public Doflamingo (NavyFighter navyFighter) {
        this.navyFighter = navyFighter;
    }
 
    public void useSword() {
        System.out.print("在多佛朗明哥操控下,");
      this.navyFighter.useSword();
    }
 
    public void fight() {
        System.out.print("在多佛朗明哥操控下,");
      this.navyFighter.fight();
    }
 
    public void useGuns() {
        System.out.print("在多佛朗明哥操控下,");
        this.navyFighter.useGuns();
    }
}

测试

public class Test {
    public static void main(String args []) {
        NavyFighter navyCaptain = new NavyCaptain();
        navyCaptain.useSword();
        navyCaptain.fight();
        navyCaptain.useGuns();
        System.out.println("-----------------------------");
        NavyFighter doflamingo = new Doflamingo(navyCaptain);
        doflamingo.useSword();
        doflamingo.fight();
        doflamingo.useSword();
    }
}

输出

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 初探设计模式六大原则

    我想用贴近生活的语句描述一下自己对六种原则的理解。也就是不做专业性的阐述,而是描述一种自己学习后的理解和感受,因为能力一般而且水平有限,也许举的例子不尽妥当,还...

    外婆的彭湖湾
  • 浅谈设计模式(二):装饰器模式|中介模式|原型模式

    装饰器模式可用来给一个类动态添加功能,将其装饰成一个新的类。这就是装饰器的概念。看到这里我们可能会想,要达到这种效果,我们用子类继承父类不就可以了吗? 没错装饰...

    外婆的彭湖湾
  • 【算法】实现栈和队列

    栈(stack) 栈(stack)是一种后进先出(LIFO)的集合类型, 即后来添加的数据会先被删除 ? 可以将其类比于下面文件的取放操作:新到的文件会被先取走...

    外婆的彭湖湾
  • 初探设计模式六大原则

    我想用贴近生活的语句描述一下自己对六种原则的理解。也就是不做专业性的阐述,而是描述一种自己学习后的理解和感受,因为能力一般而且水平有限,也许举的例子不尽妥当,还...

    外婆的彭湖湾
  • 自定义注解与常用设计模式

    注解分为:内置注解,自定义注解。内置注解就是JDK 自带的,而自定义注解则是自己定义的比如许多框架(spring) 用到的

    斯文的程序
  • 设计模式-里氏替换

    先来看个最正宗的定义 如果对每一个类型为S的对象o1,都有类型为T的对 象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变 ...

    yiduwangkai
  • Java基础-day11-接口;多态案例练习

    Java基础-day11-接口&多态案例练习 题目要求1(多态): 定义家类 方法:饲养动物 动物类: 属性:年龄、姓名 方法:吃饭、睡觉 猫类、狗类、猪类均为...

    Java帮帮
  • 图解Java设计模式

    只要是在类中用到了对方,那么他们之间就存在依赖关系。如果没有对方,连编 绎都通过不了。

    编程之心
  • 第76节:Java中的基础知识

    设置环境,安装操作系统,安装备份,就是镜像,jdk配置环境,eclipse下载解压即可使用,下载tomcat

    达达前端
  • 依赖注入容器-- Autofac

    Autofac---Autofac是一款IOC框架,比较于其他的IOC框架,如Spring.NET,Unity,Castle等等所包含的,它很轻量级性能上非常高...

    小世界的野孩子

扫码关注云+社区

领取腾讯云代金券