java 常用十种设计模式示例归纳 | 已打包请带走

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。

GitHub地址

DesignPattern

文章说明

一个Demo,集合常用的十种设计模式,每个模式使用易被人们接受的案例讲述,按模式分包,使用设计模式前后对比,界面显示定义讲解,让你更深刻的了解每种设计模式。
大部分案例来自张鸿洋的博客。如有错误欢迎指正,如有侵权,请联系我删除。

项目结构

包结构.png

设计模式分为三种类型,共23种:

MainActivity.png

博客目录

对应模式所在的包

模式分析

1. 观察者模式

定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。

  • 对于JDK或者Andorid中都有很多地方实现了观察者模式,比如XXXView.addXXXListenter , 当然了 XXXView.setOnXXXListener不一定是观察者模式,因为观察者模式是一种一对多的关系,对于setXXXListener是1对1的关系,应该叫回调。

  • 专题接口:Subject.java ;

    /**
     * 注册一个观察者
     */
   public void registerObserver(Observer observer);
   /**
    * 移除一个观察者
    */
   public void removeObserver(Observer observer);
   /**
    * 通知所有观察者
    */
   public void notifyObservers();
  • 3D服务号的实现类:ObjectFor3D.java

     @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
    @Override
    public void removeObserver(Observer observer) {
        int index = observers.indexOf(observer);
        if (index >= 0) {
            observers.remove(index);
        }
    }
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(msg);
        }
    }
    /**
     * 主题更新信息
     */
    public void setMsg(String msg) {
        this.msg = msg;
        notifyObservers();
    }
    
  • 所有观察者需要实现此接口:Observer.java

public ObserverUser1(Subject subject) {
       subject.registerObserver(this);
   }
   @Override
   public void update(String msg) {
       Log.e("-----ObserverUser1 ", "得到 3D 号码:" + msg + ", 我要记下来。 ");
   }
  • 最后测试:ObserverActivity.java

    // 创建服务号
    objectFor3D = new ObjectFor3D();
    // 创建两个订阅者
    observerUser1 = new ObserverUser1(objectFor3D);
    observerUser2 = new ObserverUser2(objectFor3D);
    // 两个观察者,发送两条信息
    objectFor3D.setMsg("201610121 的3D号为:127");
    objectFor3D.setMsg("20161022 的3D号为:000");
    

2. 工厂模式

简单列一下这个模式的家族:

  • 1、静态工厂模式

    • 这个最常见了,项目中的辅助类,TextUtil.isEmpty等,类+静态方法。
  • 2、简单工厂模式(店里买肉夹馍)

    • 定义:通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
    • 根据类型直接创建肉夹馍:SimpleRoujiaMoFactory.java
    public RoujiaMo creatRoujiaMo(String type) {
        RoujiaMo roujiaMo = null;
        switch (type) {
            case "Suan":
                roujiaMo = new ZSuanRoujiaMo();
                break;
            case "La":
                roujiaMo = new ZLaRoujiaMo();
                break;
            case "Tian":
                roujiaMo = new ZTianRoujiaMo();
                break;
            default:// 默认为酸肉夹馍
                roujiaMo = new ZSuanRoujiaMo();
                break;
        }
        return roujiaMo;
    }
    
  • 3、工厂方法模式(开分店)

    • 定义:定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式把类实例化的过程推迟到子类。
    • 对比定义:
    • 1、定义了创建对象的一个接口:public abstract RouJiaMo sellRoujiaMo(String type);
    • 2、由子类决定实例化的类,可以看到我们的馍是子类生成的。
  • 提供创建肉夹馍店抽象方法:RoujiaMoStore.java

    public abstract RoujiaMo sellRoujiaMo(String type);
    
  • 具体实现抽象方法:XianRoujiaMoStore.java

  • 分店依旧使用简单工厂模式:XianSimpleRoujiaMoFactory.java

  • 4、抽象工厂模式(使用官方提供的原料)

    • 定义:提供一个接口,用于创建相关的或依赖对象的家族,而不需要明确指定具体类。
    • 对比定义:
      • 1、提供一个接口:public interface RouJiaMoYLFactroy
      • 2、用于创建相关的或依赖对象的家族 public Meat createMeat();public YuanLiao createYuanliao();我们接口用于创建一系列的原材料。
    • 创建用于提供原料的接口工厂:RoujiaMoYLFactory.java
    • 各自分店实现接口,完成原料提供:XianRoujiaMoYLFoctory.java
    • 准备时,使用官方的原料:RoujiaMo.java
    /**
       * 准备工作
       */
    public void prepare(RoujiaMoYLFactory roujiaMoYLFactory) {
           Meet meet = roujiaMoYLFactory.creatMeet();
           YuanLiao yuanLiao = roujiaMoYLFactory.creatYuanLiao();
           Log.e("---RoujiaMo:", "使用官方的原料 ---" + name + ": 揉面-剁肉-完成准备工作 yuanLiao:"+meet+"yuanLiao:"+yuanLiao);
    }
    

3. 单例设计模式

单例模式主要是为了避免因为创建了多个实例造成资源的浪费,且多个实例由于多次调用容易导致结果出现错误,而使用单例模式能够保证整个应用中有且只有一个实例

  • 定义:只需要三步就可以保证对象的唯一性

    • (1) 不允许其他程序用new对象
    • (2) 在该类中创建对象
    • (3) 对外提供一个可以让其他程序获取该对象的方法
  • 对比定义:

  • (1) 私有化该类的构造函数

  • (2) 通过new在本类中创建一个本类对象

  • (3) 定义一个公有的方法,将在该类中所创建的对象返回

  • 饿汉式[可用]:SingletonEHan.java

  • 含懒汉式[双重校验锁 推荐用]:SingletonLanHan.java

private SingletonLanHan() {}
private static SingletonLanHan singletonLanHanFour;
public static SingletonLanHan getSingletonLanHanFour() {
if (singletonLanHanFour == null) {
synchronized (SingletonLanHan.class) {
if (singletonLanHanFour == null) {
singletonLanHanFour = new SingletonLanHan();
}
}
}
return singletonLanHanFour;
}


- 内部类[推荐用]:[SingletonIn.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/singleton/inclass/SingletonIn.java)
- 枚举[推荐用]:[SingletonEnum.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/singleton/enums/SingletonEnum.java)

![单例设计模式.png](http://upload-images.jianshu.io/upload_images/1354448-756908a3ca147593.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/240)


###4. 策略模式
> 策略模式:定义了算法族,分别封装起来,让它们之间可相互替换,此模式让算法的变化独立于使用算法的客户。

- 以创建游戏角色为例子:
    - 最初的游戏角色的父类:[Role.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/strategy/old/Role.java)
    - 发现有重复代码后,重构后的父类:[Role.java](https://github.com/youlookwhat/DesignPattern/blob/master/app/src/main/java/com/example/jingbin/designpattern/strategy/better/Role.java)
- 总结:
    - 1、封装变化(把可能变化的代码封装起来)
    - 2、多用组合,少用继承(我们使用组合的方式,为客户设置了算法)
    - 3、针对接口编程,不针对实现(对于Role类的设计完全的针对角色,和技能的实现没有关系)
- 最后测试:创建角色:

```java
RoleA roleA = new RoleA("---A");
roleA.setiDisplayBehavior(new DisplayYZ())
      .setiAttackBehavior(new AttackXL())
      .setiDefendBehavior(new DefendTMS())
      .setiRunBehavior(new RunJCTQ());
roleA.display();// 样子
roleA.attack();// 攻击
roleA.run();// 逃跑
roleA.defend();// 防御

5. 适配器模式

定义:将一个类的接口转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以相互合作。这个定义还好,说适配器的功能就是把一个接口转成另一个接口。

  • 以充电器为实例: 手机充电器一般都是5V左右吧,咱天朝的家用交流电压220V,所以手机充电需要一个适配器(降压器)

  • 一部手机: Mobile.java

  • 手机依赖一个提供5V电压的接口: V5Power.java

  • 我们拥有的是220V家用交流电: V220Power.java

  • 适配器,完成220V转5V的作用V5PowerAdapter.java

  • 最后测试:给手机冲个电:

    Mobile mobile = new Mobile();
    V5Power v5Power = new V5PowerAdapter(new V200Power());
    mobile.inputPower(v5Power);
    

6. 命令模式

定义:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。(简化: 将请求封装成对象,将动作请求者和动作执行者解耦。)

  • 需求:最近智能家电很火热,假设现在有电视、电脑、电灯等家电,现在需要你做个遥控器控制所有家电的开关,要求做到每个按钮对应的功能供用户个性化,对于新买入家电要有非常强的扩展性。
  • 1、家电的API:Door.java
  • 2、把命令封装成类:
  • 3、遥控器:ControlPanel.java
  • 4、定义一个命令,可以干一系列的事情:QuickCommand.java
QuickCommand quickCloseCommand = new QuickCommand(new Command[]{new LightOffCommand(light), new ComputerOffCommand(computer), new DoorCloseCommand(door)});
controlPanel.setCommands(6, quickOpenCommand);
controlPanel.keyPressed(6);
controlPanel.setCommands(0, new DoorOpenCommand(door));// 开门
controlPanel.keyPressed(0);
命令模式

7. 装饰者模式

装饰者模式:若要扩展功能,装饰者提供了比集成更有弹性的替代方案,动态地将责任附加到对象上。

  • 先简单描述下装饰者模式发挥作用的地方,当我们设计好了一个类,我们需要给这个类添加一些辅助的功能,并且不希望改变这个类的代码,这时候就是装饰者模式大展雄威的时候了。这里还体现了一个原则:类应该对扩展开放,对修改关闭。

  • 需求:设计游戏的装备系统,基本要求,要可以计算出每种装备在镶嵌了各种宝石后的攻击力和描述:

  • 1、装备的超类:IEquip.java

  • 2、各个装备的实现类:

  • 3、装饰品的超类(装饰品也属于装备):IEquipDecorator.java

  • 4、装饰品的实现类:

  • 5、最后测试:计算攻击力和查看描述:

    Log.e("---", "一个镶嵌2颗红宝石,1颗蓝宝石的靴子: ");
    IEquip iEquip = new RedGemDecotator(new RedGemDecotator(new BlueGemDecotator(new ShoeEquip())));
    Log.e("---", "攻击力:" + iEquip.caculateAttack());
    Log.e("---", "描述语:" + iEquip.description());
    

8. 外观模式

定义:提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层的接口,让子系统更容易使用。其实就是为了方便客户的使用,把一群操作,封装成一个方法。

  • 需求:我比较喜欢看电影,于是买了投影仪、电脑、音响、设计了房间的灯光、买了爆米花机,然后我想看电影的时候,我需要一键观影和一键关闭。

  • 每个设备类的开关等操作:

  • eg: 爆米花机:PopcornPopper.java

  • 电影院类:HomeTheaterFacade.java

    /**
     * 一键观影
     */
    public void watchMovie() {
        computer.on();
        light.down();
        popcornPopper.on();
        popcornPopper.makePopcorn();
        projector.on();
        projector.open();
        player.on();
        player.make3DListener();
    }
    
  • 最后测试:一键观影:

    new HomeTheaterFacade(computer, light, player, popcornPopper, projector).watchMovie();
    

9. 模板方法模式

定义:定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。

  • 需求:简单描述一下:本公司有程序猿、测试、HR、项目经理等人,下面使用模版方法模式,记录下所有人员的上班情况

  • 模板方法模式中的三类角色

  • 1、具体方法(Concrete Method)

  • 2、抽象方法(Abstract Method)

  • 3、钩子方法(Hook Method)

  • 工人的超类:Worker.java

    // 具体方法
    public final void workOneDay() {
        Log.e("workOneDay", "-----------------work start----------------");
        enterCompany();
        work();
        exitCompany();
        Log.e("workOneDay", "-----------------work end----------------");
    }
    // 工作  抽象方法
    public abstract void work();
    // 钩子方法
    public boolean isNeedPrintDate() {
        return false;
    }
    private void exitCompany() {
        if (isNeedPrintDate()) {
            Log.e("exitCompany", "---" + new Date().toLocaleString() + "--->");
        }
        Log.e("exitCompany", name + "---离开公司");
    }
    
  • 程序员实现类(可得知时间):ITWorker.java

    /**
     * 重写父类的此方法,使可以查看离开公司时间
     */
    @Override
    public boolean isNeedPrintDate() {
        return true;
    }
    
  • 最后测试:

    • 查看所有人员的工作情况:

      QAWorker qaWorker = new QAWorker("测试人员");
      qaWorker();
      HRWorker hrWorker = new HRWorker("莉莉姐");
      hrWorker.workOneDay();
      ...
      
    • 查看程序猿离开公司的时间:

      ITWorker itWorker = new ITWorker("jingbin");
      itWorker.workOneDay();
      

10. 状态模式

定义:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

  • 定义又开始模糊了,理一下,当对象的内部状态改变时,它的行为跟随状态的改变而改变了,看起来好像重新初始化了一个类似的。

  • 需求:已自动售货机为例(有已投币、未投币等状态和投币、投币等方法)

  • 最初实现待改进的售货机:VendingMachine.java

  • 改进后的售货机(更具有延展性):VendingMachineBetter.java

    // 放钱
    public void insertMoney() {
        currentState.insertMoney();
    }
    // 退钱
    public void backMoney() {
        currentState.backMoney();
    }
    // 转动曲柄
    public void turnCrank() {
        currentState.turnCrank();
        if (currentState == soldState || currentState == winnerState) {
            currentState.dispense();//两种情况会出货
        }
    }
    // 出商品
    public void dispense() {
        Log.e("VendingMachineBetter", "---发出一件商品");
        if (count > 0) {
            count--;
        }
    }
    // 设置对应状态
    public void setState(State state) {
        this.currentState = state;
    }
    
  • 状态的接口:State.java

  • 对应状态的接口实现类:

  • 改进后的售货机测试:

    // 初始化售货机,且里面有3个商品
    VendingMachineBetter machineBetter = new VendingMachineBetter(3);
    machineBetter.insertMoney();
    machineBetter.turnCrank();
    

参考链接

Thanks

End

感兴趣的小伙伴可以Star哦~

GitHub:DesignPattern

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,105评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,296评论 18 399
  • 面向对象的六大原则 单一职责原则 所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于...
    JxMY阅读 885评论 1 3
  • 助学贷款是国家对普通高校贫困家庭学生资助所采取的一项重大措施,对于广大寒门学子是一项非常好的政策。 由于在校学习期...
    玩头条的小伙子阅读 5,854评论 7 10
  • 首先导入两张图片 图片一 图片二 接着复制图片一的背景图层到图片二的位置。 提示,红色圈内可以选择复制到哪里! 之...
    奥哈阅读 408评论 0 5