浅谈23种设计模式(4/23)

2018-04-18  本文已影响0人  ls_cherish

设计模式汇总

创建型模式

这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用新的运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

  1. 工厂模式(Factory Pattern)
  2. 抽象工厂模式(Abstract Factory Pattern)
  3. 单例模式(Singleton Pattern)
  4. 建造者模式(Builder Pattern)
  5. 原型模式(Prototype Pattern)

结构型模式

这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。

  1. 适配器模式(Adapter Pattern)
  2. 桥接模式(Bridge Pattern)
  3. 过滤器模式(Filter、Criteria Pattern)
  4. 组合模式(Composite Pattern)
  5. 装饰器模式(Decorator Pattern)
  6. 外观模式(Facade Pattern)
  7. 享元模式(Flyweight Pattern)
  8. 代理模式(Proxy Pattern)

行为型模式

这些设计模式特别关注对象之间的通信。

  1. 责任链模式(Chain of Responsibility Pattern)
  2. 命令模式(Command Pattern)
  3. 解释器模式(Interpreter Pattern)
  4. 迭代器模式(Iterator Pattern)
  5. 中介者模式(Mediator Pattern)
  6. 备忘录模式(Memento Pattern)
  7. 观察者模式(Observer Pattern)
  8. 状态模式(State Pattern)
  9. 空对象模式(Null Object Pattern)
  10. 策略模式(Strategy Pattern)
  11. 模板模式(Template Pattern)
  12. 访问者模式(Visitor Pattern)

1 适配器模式

定义

将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。
我们用图来形象的解释这一概念:

1.png 2.png

这里的重点在于,老接口有特殊功能,我们不忍舍弃,比如只有欧洲的插头(老接口)能从插座里获得电,没有电我们过不下去。这个功能我们无法舍弃
为了继续享受这个特殊功能,我们使用适配器模式,让我们手中的美国的插头(新接口),拥有老接口的能力。

本例使用场景

例如一个美国人说英语,一个中国人说中文,为了跟美国人做生意,说英文这个功能我们无法舍弃,但是我们是中国人,天生不会说英文。于是两者想要交流,就需要一个适配器,来充当沟通两者的工作。现在,我们希望让一个能说中国话的个体(实现说中文的接口的类),开口说英文。

角色

类适配器

适配器有两种主要的实现,我们先看第一种——类适配器

具体实现

// 被适配类,已存在的、具有还有用处的特殊功能、但不符合我们既有的标准接口的类 
//——本例中即为一个会说f**k的美国人(你可以看作这个美国人实现了说英文的接口,不过这个无关紧要,省略),他说的话我们听不懂
class American{  
    public void speak() {  
        System.out.println("f**k");  
    }  
}  
// 目标接口,或称为标准接口  ——这里是一个说中文能力的接口,他定义了方法“说话”。
interface SpeakChinese {  
    public void shuoHua();  
} 
// 具体目标类,只提供普通功能 ——这里我们的具体实现是一个中国人类,他实现了说中国话的接口
class Chinese implements SpeakChinese {  
    public void shuoHua() {  
        System.out.println("敲里吗");  
    }  
}  
// 适配器类,继承了被适配类,同时实现标准接口  ——现在我们觉得,那个美国人说的四字真言好拽哦,我也要学会,于是我们定义了适配器
class Adapter extends American implements SpeakChinese {  
    public void shuoHua() {  
        super.speak();  
    }  
}  

测试

现在我们定义一个laoWang,老王是一个Chinese,他会说话这个方法。
然后再定义一个适配器,看看适配器能不能用中文的说话方法,说出英文来。

// 测试类public class Client {  
    public static void main(String[] args) {  
        // 使用普通功能类  
        SpeakChinese laoWang= new Chinese();  
        laoWang.shuoHua();  
          
        // 使用特殊功能类,即适配类  
        SpeakChinese adapter = new Adapter();  
        adapter.shuoHua();  
    }  
}  

测试结果:

敲里吗
f**k

很棒,现在用了适配器,适配器用说中文的方式,说出了这句著名的英文,现在我们迂回得到了说这四个字母的能力,可以去找外国友人交流感情了。

对象适配器

另外一种适配器模式是对象适配器,它不是使用多继承或继承再实现的方式,而是使用直接关联,或者称为委托的方式。

具体实现

其他目标类和被适配类都一样,就是适配器类的定义方式有所不同:

// 适配器类,直接关联被适配类,同时实现标准接口  
class Adapter implements SpeakChinese {  
    // 直接关联被适配类  
    private American american;  
      
    // 可以通过构造函数传入具体需要适配的被适配类对象  
    public Adapter (American american) {  
        this.american = american;  
    }  
      
    public void shuoHua() {  
        // 这里是使用委托的方式完成特殊功能  
        this.american.speak();  
    }  
}  

测试

在这里,我们为了更灵活一点,定义了一个加州人,加州人和外面那些妖艳贱货不一样,他们比较优雅,一般喜欢说hello。

class Californian extends American{  
    public void speak() {  
        System.out.println("hello");  
    }  
} 
public class Client {  
    public static void main(String[] args) {  
        // 使用普通功能类  
        SpeakChinese laoWang = new Chinese();  
        laoWang.shuoHua();  
          
        // 使用特殊功能类,即适配类,  
        // 需要先创建一个被适配类的对象作为参数  
        American tom = new Californian(){}
        Target adapter = new Adapter(tom);  
        adapter.shuoHua();  
    }  
}  

测试结果

敲里吗
hello

同样的,我们用适配器获得了像加州人那样优雅的说英文的能力。对象适配器相对于类适配器来说比较灵活,我们可以复用这个适配器,通过传参的不同,得到德克萨斯人,弗吉尼亚人,佛罗里达人的说话方式。

优劣

模式总结

优点

缺点

2 策略模式

定义

首先我们理解策略的概念,策略就是一组算法,一种实现。策略模式定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。

本例使用场景

例如神机妙算的诸葛亮,他的每个锦囊,就是一个策略,现在诸葛亮给了关羽三个锦囊——锦囊A,锦囊B,锦囊C,告诉关羽如果敌军数量在一万到三万以内,打开锦囊A;敌军数量在三万到五万之间,打开锦囊B;还更多的话,打开锦囊C。

角色

具体实现

//抽象策略类 锦囊接口
public interface JinNang {
    /**
     * 策略方法 打开锦囊
     */
    public void openJinNang();
}
//具体策略类 锦囊A
public class JinNangA implements JinNang {

    @Override
    public void openJinNang(Integer enermyNum) {
        System.out.println("朝敌军使用抛掷粪便攻击,杀敌20%!伤敌数量为:"+enermyNum*0.2);
    }

}

//具体策略类 锦囊B
public class JinNangB  implements JinNang {

    @Override
    public void openJinNang(Integer enermyNum) {
        System.out.println("朝敌军播放难忘今宵以瓦解敌军意志,杀敌50%!伤敌数量为:"+enermyNum*0.5);
    }

}

//具体策略类 锦囊C
public class JinNangC implements JinNang {

    @Override
    public void openJinNang(Integer enermyNum) {
        System.out.println("丫的还不快跑!?");
    }

}
//环境角色类--关羽
public class GuanYu{
    //持有一个具体策略的对象
    private JinNang jinNang;

    /**
     * 策略方法
     */
    public void fight(Integer enermyNum){
        if (enermyNum >10000&enermyNum<30000){
            jinNang=new JinNangA();
        } else if (enermyNum >30000&enermyNum<50000){
            jinNang=new JinNangB();
        } else{
            jinNang=new JinNangC();
        }
        jinNang.openJinNang(enermyNum);
    }

}

测试

好的,现在我们的关羽跨上赤兔马,拎起青龙刀,来到了阵前,对面分别出动了两个师,四个师,十个师的兵力干他!即便如此,我们的小英雄也A了上去!

public static void main(String[] args) {
        GuanYu guanYu = new GuanYu();
        guanYu.fight(20000);
        guanYu.fight(40000);
        guanYu.fight(100000);
    }

测试结果
显而易见,测试结果是:

朝敌军使用抛掷粪便攻击,杀敌20%!伤敌数量为:4000.0
朝敌军播放难忘今宵以瓦解敌军意志,杀敌50%!伤敌数量为:20000.0
丫的还不快跑!?

模式总结

优劣

优点

缺点

3 外观模式

定义

外观模式(Facade Pattern)又称为门面模式,它为系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得子系统更加容易使用。该模式可以隐藏系统的复杂性,提供了客户端请求的简化方法和对现有系统类方法的委托调用。

本例使用场景

蜀汉集团总经理诸葛亮为了光复中原,推出了“三个臭皮匠”阵法,由关羽张飞赵云压阵,在砍人的时候充当蜀军的门面。于是在群P的时候,对于他们的客户(敌人)来说,和外观(阵法)交互(打架)并不需要知道他们内在的细节,许多繁琐的逻辑,已经被门面封装了。

角色

具体实现

我们先来定义三个子系统:关张赵

/**
 * 子系统关羽
 */
public class GuanYu {
    //剪刀
    public void jianDao(){
        System.out.println("关羽出剪刀");
    }
    public void shiTou(){
        System.out.println("关羽出石头");
    }
    public void bu(){
        System.out.println("关羽出布");
    }
}
/**
 * 子系统赵云
 */
public class ZhaoYun{
    //剪刀
    public void jianDao(){
        System.out.println("赵云出剪刀");
    }
    public void shiTou(){
        System.out.println("赵云出石头");
    }
    public void bu(){
        System.out.println("赵云出布");
    }
}
/**
 * 子系统张飞
 */
public class ZhangFei {
    //剪刀
    public void jianDao(){
        System.out.println("张飞出剪刀");
    }
    public void shiTou(){
        System.out.println("张飞出石头");
    }
    public void bu(){
        System.out.println("张飞出布");
    }
}

接下来定义统一的外观类——三个臭皮匠阵法,这个阵法有三个绝技,青龙腾,白龙腾和黑龙腾,至于技能内到底怎么实现的,你们这些凡人就不要在意了,总之发动起来,天上都是龙,你们只要负责喊666就可以了。

public class ThreeChouPiJiang{
    private GuanYu guanYu;
    private ZhangFei zhangFei;
    private ZhaoYun zhaoYun;
    ThreeChouPiJiang(){
        guanYu =new GuanYu();
        zhangFei = new ZhangFei();
        zhaoYun = new ZhaoYun();
    }
    //青龙腾
    public void qingLongTeng(){
        zhangFei.bu();
        zhaoYun.bu();
        guanYu.shiTou();
        System.out.println("青龙腾:关羽输了,关羽出去砍人;");
        System.out.println("BO~BO~BO~经费燃烧中~ =============");
    }
    //黑龙腾
    public void heiLongTeng(){
        guanYu.bu();
        zhaoYun.bu();
        zhangFei.shiTou();
        System.out.println("黑龙腾:张飞输了,张飞出去砍人;");
        System.out.println("BO~BO~BO~经费燃烧中~ =============");
    }
    //白龙腾
    public void baiLongTeng(){
        guanYu.bu();
        zhangFei.bu();
        zhaoYun.shiTou();
        System.out.println(":赵云输了,赵云出去砍人;");
        System.out.println("BO~BO~BO~经费燃烧中~ =============");
    }
}

测试

好了,我们的阵法已经定义好了,现在诸葛亮意气风发,决定要北伐中原,我们的三个臭皮匠阵法,开始发挥威力:

public static void main(String[] args) {
    ThreeChouPiJiang threeChouPiJiang = new ThreeChouPiJiang();
    ThreeChouPiJiang threeChouPiJiang = new ThreeChouPiJiang();
    threeChouPiJiang.baiLongTeng();
    threeChouPiJiang.qingLongTeng();
    threeChouPiJiang.heiLongTeng();
}

测试结果:

关羽出布
张飞出布
赵云出石头
白龙腾:赵云输了,赵云出去砍人;
BO~BO~BO~经费燃烧中~ =============
张飞出布
赵云出布
关羽出石头
青龙腾:关羽输了,关羽出去砍人;
BO~BO~BO~经费燃烧中~ =============
关羽出布
赵云出布
张飞出石头
黑龙腾:张飞输了,张飞出去砍人;
BO~BO~BO~经费燃烧中~ =============

威力果然是十分惊人啊!对于敌人而言,他们只知道三个臭皮匠阵法使用了绝招,根本不会知道绝招内部居然是这三个货用剪刀石头布搞出来的。在顶级特效的加持下,这个门面还是十分整洁十分威风十分唬人的。

模式总结

外观模式的目的不是给予子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统。
外观模式的本质是:封装交互,简化调用。

优劣

优点

缺点

4 观察者模式

定义

在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。
简单来说,其实就是发布订阅模式,发布者发布信息,订阅者获取信息,订阅了就能收到信息,没订阅就收不到信息。

本例使用场景

例如微信公众号服务,不定时发布一些消息,关注公众号就可以收到推送消息,取消关注就收不到推送消息。

角色

具体实现

/***
 * 抽象被观察者接口
 * 声明了添加、删除、通知观察者方法
 */
public interface Observerable {
    
    public void registerObserver(Observer o);//新增订阅人
    public void removeObserver(Observer o);//删除订阅人
    public void notifyObserver();//发布消息
    
}
/***
 * 抽象观察者
 * 定义了一个update()方法,当被观察者调用notifyObservers()方法时,观察者的update()方法会被回调。
 */
public interface Observer {
    public void update(String message);//更新消息
}
/**
 * 被观察者,也就是微信公众号服务
 * 实现了Observerable接口,对Observerable接口的三个方法进行了具体实现
 */
public class WechatServer implements Observerable {
    //注意到这个List集合的泛型参数为Observer接口,设计原则:面向接口编程而不是面向实现编程
    private List<Observer> list;
    private String message;
    
    public WechatServer() {
        list = new ArrayList<Observer>();
    }
    
    @Override
    public void registerObserver(Observer o) {
        
        list.add(o);
    }
    
    @Override
    public void removeObserver(Observer o) {
        if(!list.isEmpty())
            list.remove(o);
    }
    
    //遍历通知
    @Override
    public void notifyObserver() {
        for(int i = 0; i < list.size(); i++) {
            Observer oserver = list.get(i);
            oserver.update(message);
        }
    }
    
    public void setInfomation(String s) {
        this.message = s;
        System.out.println("微信服务更新消息: " + s);
        //消息更新,通知所有观察者
        notifyObserver();
    }
}
/**
 * 观察者
 * 实现了update方法
 */
public class User implements Observer {

    private String name;
    private String message;
    
    public User(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String message) {
        this.message = message;
        read();
    }
    
    public void read() {
        System.out.println(name + " 收到推送消息: " + message);
    }
    
}

测试

首先注册了三个用户,ZhangSan、LiSi、WangWu。公众号发布了一条消息"PHP是世界上最好用的语言!",三个用户都收到了消息。

用户ZhangSan看到消息后颇为震惊,果断取消订阅,这时公众号又推送了一条消息,此时用户ZhangSan已经收不到消息,其他用户

还是正常能收到推送消息。

public class Test {
    
    public static void main(String[] args) {
        WechatServer server = new WechatServer();
        
        Observer userZhang = new User("ZhangSan");
        Observer userLi = new User("LiSi");
        Observer userWang = new User("WangWu");
        
        server.registerObserver(userZhang);
        server.registerObserver(userLi);
        server.registerObserver(userWang);
        server.setInfomation("PHP是世界上最好用的语言!");
        
        System.out.println("----------------------------------------------");
        server.removeObserver(userZhang);
        server.setInfomation("JAVA是世界上最好用的语言!");
        
    }
}

测试结果:
[图片上传失败...(image-ef81d4-1524021404702)]

上一篇 下一篇

猜你喜欢

热点阅读