JAVA设计模式

一、设计模式的分类

什么是设计模式:在某些场景下,针对某类问题的某种通用的解决方案
特点: 通用、可复用

设计模式有哪些

  • 创建型模式 :对象实例化的模式,用于解耦对象的实例化过程。5种
    工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
  • 结构型模式 :把类或对象结合在一起形成一个更大的结构。7种
    适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式
  • 行为型模式 : 类与对象交互,责任划分及算法。11种
    策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
    共23种
  • 额外两种: 并发型模式, 线程池模式

二、设计模式的六大原则

总原则:开闭原则(Open Close Principle)
对扩展开放,对修改关闭。使程序的扩展性好,易于维护和升级, 使用接口和抽象类等, 实现热插拔效果。

1、单一职责原则
每个类应该实现单一的职责,解耦。

2、里氏替换原则(Liskov Substitution Principle)
对“开-闭”原则的补充,对实现抽象化的具体步骤的规范。
子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。

3、依赖倒转原则(Dependence Inversion Principle)
开闭原则的基础,面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。

4、接口隔离原则(Interface Segregation Principle)
每个接口中不存在子类用不到却必须实现的方法,如果不然,就要将接口拆分。使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好。

5、迪米特法则(最少知道原则)(Demeter Principle)
一个类对自己依赖的类知道的越少越好。无论被依赖的类多么复杂,都应将逻辑封装在方法的内部,通过public方法提供给外部。这样当被依赖的类变化时,才能最小的影响该类。

6、合成复用原则(Composite Reuse Principle)
尽量首先使用聚合的方式,而不是使用继承


常见模式介绍

1) 单例模式

它核心结构只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。

应用场景:如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

懒汉式

class LazyMan{

    private static LazyMan lm;
    private LazyMan(){};
    
    public static LazyMan getInstance(){
        if (lm == null){
            synchronized (LazyMan.class) {
                if (lm == null) {
                    lm = new LazyMan();
                }
            }
        }
        return lm;
    }
}

饿汉式

class HungeryMan{

    private static HungeryMan hm;
    
    private HungeryMan(){
        hm = new HungeryMan();
    };
    
    public static HungeryMan getInstance(){
        return hm;
    }
}
2) 工厂方法模式

工厂模式主要是为创建对象提供了接口。
应用场景:
a、 在编码时不能预见需要创建哪种类的实例。
b、 系统不应依赖于产品类实例如何被创建、组合和表达的细节。

简单工厂模式不属于23种设计模式范畴。
简单工厂模式的问题:类的创建依赖工厂类,若想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,就有了工厂方法模式,创建一个工厂接口和创建多个工厂实现类,一旦需要增加新的功能,直接增加新的工厂类,不需要修改之前的代码。

一个顶级接口

public interface Sender {  

    public void Send();  
}  

两个实现类:

public class MailSender implements Sender {  

    @Override  
    public void Send() {  
        System.out.println("this is mailsender!");  
    }  
}  

public class SmsSender implements Sender {  

    @Override  
    public void Send() {  
        System.out.println("this is sms sender!");  
    }  
}  

两个工厂类:

public class SendMailFactory implements Provider {  
      
    @Override  
    public Sender produce(){  
        return new MailSender();  
    }  
}  
[java] view plaincopy
public class SendSmsFactory implements Provider{  
  
    @Override  
    public Sender produce() {  
        return new SmsSender();  
    }  
}  

再提供一个接口:

public interface Provider {  
    public Sender produce();  
}  

测试类:

public class Test {  
  
    public static void main(String[] args) {  
        Provider provider = new SendMailFactory();  
        Sender sender = provider.produce();  
        sender.Send();  
    }  
}  
3) 策略模式

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

应用场景:
a、 一件事情,有很多方案可以实现。
b、我可以在任何时候,决定采用哪一种实现。
c.、未来可能增加更多的方案。
d、 策略模式让方案的变化不会影响到使用方案的客户。

策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口。可以设计一个抽象类(辅助类可有可无),提供辅助函数

首先统一接口:

public interface ICalculator {  

    public int calculate(String exp);  
}  

辅助类:

public abstract class AbstractCalculator {  
      
    public int[] split(String exp,String opt){  

        String array[] = exp.split(opt);  
        int arrayInt[] = new int[2];  
        arrayInt[0] = Integer.parseInt(array[0]);  
        arrayInt[1] = Integer.parseInt(array[1]);  
        return arrayInt;  
    }  
}  

三个实现类:

public class Plus extends AbstractCalculator implements ICalculator {  
  
    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"\\+");  
        return arrayInt[0]+arrayInt[1];  
    }  
}  

public class Minus extends AbstractCalculator implements ICalculator {  
  
    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"-");  
        return arrayInt[0]-arrayInt[1];  
    }  
  
}  

public class Multiply extends AbstractCalculator implements ICalculator {  
  
    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"\\*");  
        return arrayInt[0]*arrayInt[1];  
    }  
} 

简单的测试类:

public class StrategyTest {  
  
    public static void main(String[] args) {  
        String exp = "2+8";  
        ICalculator cal = new Plus();  
        int result = cal.calculate(exp);  
        System.out.println(result);  
    }  
}  

输出:10

4) 观察者模式。

又被称作发布/订阅模式,定义了对象间一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

应用场景:
a、对一个对象状态的更新,需要其他对象同步更新,而且其他对象的数量动态可变。
b、对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节。

一个Observer接口:

public interface Observer {  

    public void update();  
}  

两个实现类:

public class Observer1 implements Observer {  
  
    @Override  
    public void update() {  
        System.out.println("observer1 has received!");  
    }  
}  

public class Observer2 implements Observer {  
  
    @Override  
    public void update() {  
        System.out.println("observer2 has received!");  
    }  
}  

Subject接口及实现类:

public interface Subject {  
      
    /*增加观察者*/  
    public void add(Observer observer);  
      
    /*删除观察者*/  
    public void del(Observer observer);  
      
    /*通知所有的观察者*/  
    public void notifyObservers();  
      
    /*自身的操作*/  
    public void operation();  
}  

public abstract class AbstractSubject implements Subject {  
  
    private Vector<Observer> vector = new Vector<Observer>();  

    @Override  
    public void add(Observer observer) {  
        vector.add(observer);  
    }  
  
    @Override  
    public void del(Observer observer) {  
        vector.remove(observer);  
    }  
  
    @Override  
    public void notifyObservers() {  
        Enumeration<Observer> enumo = vector.elements();  
        while(enumo.hasMoreElements()){  
            enumo.nextElement().update();  
        }  
    }  
}  

public class MySubject extends AbstractSubject {  
  
    @Override  
    public void operation() {  
        System.out.println("update self!");  
        notifyObservers();  
    }  
  
}  

测试类:

public class ObserverTest {  
  
    public static void main(String[] args) {  

        Subject sub = new MySubject();  
        sub.add(new Observer1());  
        sub.add(new Observer2());  
          
        sub.operation();  
    }  
}  

输出:
update self!
observer1 has received!
observer2 has received!

5) 装饰器模式

给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例

应用场景:
a、需要扩展一个类的功能。
b、动态的为一个对象增加、撤销功能。(继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错

接口

public interface Sourceable {  

    public void method();  
}  

原始实现类

public class Source implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("the original method!");  
    }  
}  

代理类

public class Decorator implements Sourceable {  
  
    private Sourceable source;  
      
    public Decorator(Sourceable source){  
        super();  
        this.source = source;  
    }  

    @Override  
    public void method() {  
        System.out.println("before decorator!");   //装饰器
        source.method();  
        System.out.println("after decorator!");  //装饰器
    }  
}  

测试类:

public class DecoratorTest {  
  
    public static void main(String[] args) {  

        Sourceable source = new Source();  
        Sourceable obj = new Decorator(source);  
        obj.method();  
    }  
}  

输出:
before decorator!
the original method!
after decorator!

6) 代理模式

给一个对象提供一个代理,并由代理对象控制对原对象的引用。它使得客户不能直接与真正的目标对象通信。
代理对象是目标对象的代表,其他需要与这个目标对象打交道的操作都是和这个代理对象在交涉,起到中介的作用,既保护了目标对象的,同时也在一定程度上面减少了系统的耦合度。

应用场景:
如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:
a、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。
b、采用一个代理类调用原有的方法,对产生的结果进行控制。
使用代理模式,可以将功能划分的更加清晰,有助于后期维护

接口

public interface Sourceable {  

    public void method();  
}  

原始实现类

public class Source implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("the original method!");  
    }  
}  

代理类

public class Proxy implements Sourceable {  
  
    private Source source;  

    public Proxy(){  
        super();  
        this.source = new Source();  
    }  

    @Override  
    public void method() {  
        before();  
        source.method();  
        atfer();  
    }  
    private void atfer() {  
        System.out.println("after proxy!");  
    }  
    private void before() {  
        System.out.println("before proxy!");  
    }  
}  

测试类:

public class ProxyTest {  
  
    public static void main(String[] args) {  

        Sourceable source = new Proxy();  
        source.method();  
    }  
}  

输出:
before proxy!
the original method!
after proxy!

代理模式,偏重因自己无法完成或自己无需关心,需要他人干涉事件流程,更多的是对对象的控制。
装饰模式,偏重对原对象功能的扩展,扩展后的对象仍是是对象本身。

7) 迭代子模式。

提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

应用场景:
当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑用迭代器模式。其实stl容器就是很好的迭代器模式的例子。

两个接口

public interface Collection {  
      
    public Iterator iterator();  
      
    /*取得集合元素*/  
    public Object get(int i);  
      
    /*取得集合大小*/  
    public int size();  
}  
public interface Iterator {  
    //前移  
    public Object previous();  
      
    //后移  
    public Object next();  
    public boolean hasNext();  
      
    //取得第一个元素  
    public Object first();  
}  

两个实现:

public class MyCollection implements Collection {  
  
    public String string[] = {"A","B","C","D","E"};  

    @Override  
    public Iterator iterator() {  
        return new MyIterator(this);  
    }  
  
    @Override  
    public Object get(int i) {  
        return string[i];  
    }  
  
    @Override  
    public int size() {  
        return string.length;  
    }  
}  
public class MyIterator implements Iterator {  
  
    private Collection collection;  
    private int pos = -1;  
      
    public MyIterator(Collection collection){  
        this.collection = collection;  
    }  
      
    @Override  
    public Object previous() {  
        if(pos > 0){  
            pos--;  
        }  
        return collection.get(pos);  
    }  
  
    @Override  
    public Object next() {  
        if(pos<collection.size()-1){  
            pos++;  
        }  
        return collection.get(pos);  
    }  
  
    @Override  
    public boolean hasNext() {  
        if(pos<collection.size()-1){  
            return true;  
        }else{  
            return false;  
        }  
    }  
  
    @Override  
    public Object first() {  
        pos = 0;  
        return collection.get(pos);  
    }  
  
}  

测试类:

public class Test {  
  
    public static void main(String[] args) {  

        Collection collection = new MyCollection();  
        Iterator it = collection.iterator();  
          
        while(it.hasNext()){  
            System.out.println(it.next());  
        }  
    }  
}  

输出:A B C D E

8) 模板方法模式

定义一个操作中的算法的骨架,将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤。

应用场景:
对于一些功能,在不同的对象身上展示不同的作用,但是功能的框架是一样的。

public abstract class AbstractCalculator {  
      
    /*主方法,实现对本类其它方法的调用*/  
    public final int calculate(String exp,String opt){  
        int array[] = split(exp,opt);  
        return calculate(array[0],array[1]);  
    }  
      
    /*被子类重写的方法*/  
    abstract public int calculate(int num1,int num2);  
      
    public int[] split(String exp,String opt){  
        String array[] = exp.split(opt);  
        int arrayInt[] = new int[2];  
        arrayInt[0] = Integer.parseInt(array[0]);  
        arrayInt[1] = Integer.parseInt(array[1]);  
        return arrayInt;  
    }  
}  
public class Plus extends AbstractCalculator {  
  
    @Override  
    public int calculate(int num1,int num2) {  
        return num1 + num2;  
    }  
}  

测试类:

public class StrategyTest {  
  
    public static void main(String[] args) {  
        String exp = "8+8";  
        AbstractCalculator cal = new Plus();  
        int result = cal.calculate(exp, "\\+");  
        System.out.println(result);  
    }  
}  

输出:10

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

推荐阅读更多精彩内容

  • 面试被人问到了设计模式,发现这方面知识漏洞还非常大,所以在这里记录备忘一下。原文:https://blog.csd...
    onlyHalfSoul阅读 491评论 0 2
  • 极客学院-23 种设计模式 Java之美[从菜鸟到高手演变]之设计模式Java之美[从菜鸟到高手演变]之设计模式 ...
    John13阅读 2,837评论 0 0
  • 前言 1、设计模式来源?答:设计模式来自于建筑领域,作为软件工程的一个分支。1995年由GOF收集整理了23种设计...
    斌林诚上阅读 5,151评论 1 13
  • 昨天听福凯说,他已经把前端项目由vuejs换成了react,因为邓鹏问了福凯一些页面上的事,估计就是这样就提到了....
    再见田园犬阅读 391评论 0 0
  • 每天学点销售学——连续第132天 倾听在沟通中有着很重要的作用,作为销售人员,更需要把倾听这门艺术运用好。 所谓倾...
    今天的面包阅读 319评论 0 1