JAVA实现23种设计模式

1. 设计模式的6大原则

  1. 开闭原则(总原则):一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展
  2. 里氏替换原则:所有引用基类对象的地方,能够透明使用其子类的对象。
  3. 依赖倒置原则:抽象不一样依赖具体类,具体类应该依赖于抽象,即是,面向接口编程
  4. 单一职责原则:一个类只负责一个功能领域中的相应职责,也就是将类进行区分、隔离。
  5. 迪米特原则(最少知道原则):一个实体类,尽可能少的和其他的类发生相互作用。
  6. 接口分离原则:使用多个专门的接口,而不使用单一的总接口,也就是在调用时,调用专门的接口即可,不需要调用不必要的接口。
  7. 合成复用原则:尽量优先使用合成/聚合的方式,而不是继承。

2.设计模式的几种类型

2.1 创建型
  • 简单工厂模式(Simple Factory)
  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(AbstractFactory)
  • 单例模式(Singleton)
  • 生成器模式(Builder)
  • 原型模式(Prototype)
2.2 结构型
  • 外观模式(Facade)
  • 适配器模式(Wrapper)
  • 桥接模式(Brige)
  • 代理模式(Proxy)
  • 装饰器模式(Decorator)
  • 享元模式(Flyweight)
2.3 行为型
  • 职责链模式(Chain of Responsibility)
  • 命令模式(Command)
  • 解释器模式(Interpreter)
  • 迭代器模式(Iterator)
  • 中介者模式(Mediator)
  • 备忘录模式(Memento)
  • 观察者模式(Observer)
  • 状态模式(State)
  • 策略模式(Strategy)
  • 模板方法模式
  • 访问者模式(Visitor)

3.各种设计模式解析

3.1 创建型
3.1.1 简单工厂模式

专门定义一个类(工厂类)来负责创建其他类的实例,不同类的实例,可以根据不同的参数的方法,来进行创建;
一般在创建时,被创建的实例,通常的都会有一个共同的父类。

在这里插入图片描述

父接口:Cola
public interface Cola {
    void name();
}
子类1PesiCola
public class PesiCola implements Cola {
    @Override
    public void name(){ System.out.println("this is a PesiCola"); }
}
子类2CocaCola
public class CocaCola implements Cola {
    @Override
    public void name(){ System.out.println("this is a CocaCola"); }
}
简单工厂:SimpleFactory
public class SimpleFactory {
    public static void main(String[] args) {
        getInstance("coca").name();
    }
    public static Cola getInstance(String name){
        if ("pesi".equals(name)) return new PesiCola();
        if ("coca".equals(name))return new CocaCola();
        return null;
    }
}
返回结果:this is a CocaCola

优点:

  • 使用者只需要给工厂类传入一个正确的约定好的参数,就可以获取你所需要的对象,而不需要知道其创建细节,一定程度上减少系统的耦合。
  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,减少开发者的记忆成本。

缺点:

  • 如果业务上添加新产品的话,就需要修改工厂类原有的判断逻辑,这其实是违背了开闭原则的。
  • 在产品类型较多时,有可能造成工厂逻辑过于复杂。所以简单工厂模式比较适合产品种类比较少而且增多的概率很低的情况。
3.1.2 工厂方法模式(工厂模式)

工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,即通过不同的工厂子类来创建不同的产品对象。

工厂父接口 ——> 工厂子类对象——>实际产品对象

附加总结:操作一个父类中的多个子类

在这里插入图片描述

父接口:Cola
public interface Cola {
    void name();
}
子类1PesiCola
public class PesiCola implements Cola {
    @Override
    public void name(){ System.out.println("this is a PesiCola"); }
}
子类2CocaCola
public class CocaCola implements Cola {
    @Override
    public void name(){ System.out.println("this is a CocaCola"); }
}
夫工厂接口:Factory
public interface Factory {
    Cola getCola();
}
子类工厂1PesiFactory
public class PesiFactory implements Factory {
    @Override
    public Cola getCola() { return new PesiCola(); }
}
子类工厂2CocaFactory
public class CocaFactory implements Factory{
    @Override
    public Cola getCola() { return new CocaCola(); }
}
测试类:
public class DemoTest {
    public static void main(String[] args) {
        // 先创建工厂
        Factory factory = new CocaFactory();
        // 再使用工厂实例,获取到实际的person实例:此时是girl
        factory.getCola().name();
    }
}  
执行结果:this is a PesiCola

优点:

  • 用户只需要关心其所需产品对应的具体工厂是哪一个即可,不需要关心产品的创建细节,也不需要知道具体产品类的类名。
  • 当系统中加入新产品时,不需要修改抽象工厂和抽象产品提供的接口,也无须修改客户端和其他的具体工厂和具体产品,而只要添加一个具体工厂和与其对应的具体产品就可以了,符合了开闭原则。

缺点:

  • 当系统中加入新产品时,除了需要提供新的产品类之外,还要提供与其对应的具体工厂类。因此系统中类的个数将成对增加,增加了系统的复杂度。
3.1.3 抽象工厂

提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

抽象工厂(包含多个不同类的方法)——>多个工厂类——>分别对多个类操作

附加总结:操作多个父类的多个子类

在这里插入图片描述

两组父子类组合
Cola父接口:Cola
public interface Cola { void name();}

Cola子类1CocaCola
public class CocaCola implements Cola {
    @Override
    public void name(){ System.out.println("this is a CocaCola"); }
}

Cola子类2PesiCola
public class PesiCola implements Cola {
    @Override
    public void name(){ System.out.println("this is a PesiCola"); }
}
===========================================================================
ColaBox父接口:ColaBox
public interface ColaBox { void getMate();}
ColaBox子类1GlassBox
public class GlassBox implements ColaBox {
    @Override
    public void getMate() { System.out.println("this is a GlassBox"); }
}
ColaBox子类2GoldBox
public class GoldBox implements ColaBox {
    @Override
    public void getMate() { System.out.println("this is GoldBox"); }
}
===================================================================================
抽象工厂夫接口:AbstractFactory 
public interface AbstractFactory {
    // 生产可乐
    Cola createCola();
    // 生产可乐瓶材质
    ColaBox createColaBox();
}
子工厂类1:CocaFactory
public class CocaFactory implements AbstractFactory {
    @Override
    public Cola createCola() {
        return new CocaCola();
    }

    @Override
    public ColaBox createColaBox() {
        return new GlassBox();
    }
}
子工厂类2:PesiFactory
public class PesiFactory implements AbstractFactory {
    @Override
    public Cola createCola() {
        return new PesiCola();
    }

    @Override
    public ColaBox createColaBox() {
        return new GoldBox();
    }
}
==================================================================
测试类
 public class DemoTest {
    public static void main(String[] args) {
        // CocaCola工厂
        AbstractFactory cocaFactory = new CocaFactory();
        // CocaCoal饮料
        cocaFactory.createCola().name(); // this is a CocaCola
        // 获取CocaCola的Box材质:GlassBox
        cocaFactory.createColaBox().getMate(); // this is a GlassBox

//        ====================================================
        // PesiCola工厂
        AbstractFactory pesiFactory = new PesiFactory();
        // PesiCola饮料
        pesiFactory.createCola().name(); // this is a PesiCola
        // 获取PesiCola的Box材质:GoldBox
        pesiFactory.createColaBox().getMate(); // this is GoldBox
    }
} 

优点:

  • 具体产品在应用层代码隔离,不需要关心产品细节。只需要知道自己需要的产品是属于哪个工厂的即可 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。

缺点:

  • 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口。
3.1.4 单例模式

确保某一个类,对外只提供一个对象。

饿汉式

在类加载的时候,就直接提供一个单例的对象

public class Hungry {
    private static Hungry hungry;
    static { hungry = new Hungry(); }
    private Hungry(){}
    public static Hungry getInstance(){ return hungry; }
}

优点:

  • 这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

缺点:

  • 在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
懒汉式

在对象第一个被使用时,再进行对象的创建

最好的单例模式,使用枚举类实现

/**
 * @Description 枚举类 , 这是最好的一种单例写法
 */
public enum LazyBest {
    INSTANCE;
    public void  doOthers(){/** 可以让单例做其他的事**/}
}

/**枚举类为什么没有反序列化的问题? **/

在序列化的时候Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。
同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。
/**
 * @Description 双重检查
 */
public class Lazy01 {
    private static Lazy01 lazy01;
    private Lazy01(){}
    public static Lazy01 getInstance(){
        if (lazy01 == null){
            synchronized (Lazy01.class){
                if (lazy01 == null){
                    lazy01 = new Lazy01();
                }
            }
        }
        return lazy01;
    }
}
缺点:
1. 反序列化的问题, 如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
2. 无法避免反射的攻击
/**
 * @Description 静态内部类
 */
public class Lazy02 {
    private Lazy02(){}
    public static Lazy02 getInstance(){ return InnerLazy02.lazy02; }
    private static class InnerLazy02{ private static final Lazy02 lazy02= new Lazy02();}
}
缺点同上
3.1.5 建造者模式

它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

优点:

  • 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者, 用户使用不同的具体建造者即可得到不同的产品对象 。
  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。
  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。

缺点:

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
3.1.6 原型模式

使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。

原型模式就像复印技术,根据原对象复印出一个新对象,并根据需求对新对象进行微调。

public class Prototype implements Cloneable {  
  
    public Object clone() throws CloneNotSupportedException {  
        Prototype proto = (Prototype) super.clone();  
        return proto;  
    }  
}  

一个原型类,只需要实现Cloneable接口,覆写clone方法,此处clone方法可以改成任意的名称,因为Cloneable接口是个空接口,你可以任意定义实现类的方法名,如cloneA或者cloneB,因为此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在Object类中,clone()是native的

补充概念:

​ **浅复制:**将一个对象复制后,基本的数据类型的变量都会重新创建,但是引用类型,还是只想原对象引用的

​ **深复制:**将一个对象复制后,不论是基本数据类型还是引用类型,都是重新创建的。

简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。

3.2 结构型
3.2.1 装饰者模式

不改变原有对象的前提下,动态地给一个对象增加一些额外的功能。

原接口—>原实现类1—>增加一个实现类2—>在实现类2中,实现类1的对象---->重写方法时增强实现类1的方法

在这里插入图片描述

初始接口:Source
public interface Source {
    void method();
}
第一次实现类1SourceImpl
public class SourceImpl implements Source {
    @Override
    public void method() {
        System.out.println("this is the SourceImpl");
    }
}
增强实现类2DecoratorSource 
public class DecoratorSource implements Source {
    // 注入了source对象
    private Source source;
    public DecoratorSource(Source source){
        super();
        this.source =source;
    }
    @Override
    public void method() {
        System.out.println("this is before.......");
        source.method();
        System.out.println("this is after.........");
    }
}
测试类:
public class DemoTest {
    public static void main(String[] args) {
        Source source = new SourceImpl();
        DecoratorSource decoratorSource = new DecoratorSource(source);
        decoratorSource.method();
    }
}
执行结果:
this is before.......
this is the SourceImpl
this is after.........

优点:

  • 比继承更加灵活:不同于在编译期起作用的继承;装饰者模式可以在运行时扩展一个对象的功能。另外也可以通过配置文件在运行时选择不同的装饰器,从而实现不同的行为。也可以通过不同的组合,可以实现不同效果。
  • 符合“开闭原则”:装饰者和被装饰者可以独立变化。用户可以根据需要增加新的装饰类,在使用时再对其进行组合,原有代码无须改变。

缺点:

  • 装饰者模式需要创建一些具体装饰类,会增加系统的复杂度。
3.2.2 适配器模式

适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

类适配器

Source中只有method1(); 目标接口Targetable中含有method1()和method2()

采用Adapter继承Source和实现Targetable,完成适配:既可以执行Source中的method1,也可以执行Targetable中的method2

在这里插入图片描述

原始类:Source
public class Source {
    public void method1(){
        System.out.println("this is Source.method1");
    }
}
目标接口:Targetable
public interface Targetable {
    void method1();
    void method2();
}
类的适配器:SourceAdapter
public class SourceAdapter extends Source implements Targetable {
    @Override
    public void method2() {
        System.out.println("this is Targetable.method2");
    }
}
测试类
public class AdapterDemoTest {
    public static void main(String[] args) {
        Targetable sourceAdapter = new SourceAdapter();
        sourceAdapter.method1();
        sourceAdapter.method2();
    }
}
执行结果:
this is Source.method1
this is Targetable.method2
对象适配器

基本适配逻辑和类适配器类似,只需要将集成Source,修改成为持有Source对象即可

以下代码仅仅SourceAdapter替换成SourceWrapper,其余未改动

对象适配器:SourceWrapper
public class SourceWrapper implements Targetable {
    private Source source;
    public SourceWrapper(Source source) {
        super();
        this.source = source;
    }
    @Override
    public void method1() {
        source.method1();
    }
    @Override
    public void method2() {
        System.out.println("this is Targetable.method2");
    }
}
测试类:
public class WrapperDemoTest {
    public static void main(String[] args) {
        Source source = new Source();
        SourceWrapper sourceWrapper = new SourceWrapper(source);
        sourceWrapper.method1();
        sourceWrapper.method2();
    }
}
执行结果:
this is Source.method1
this is Targetable.method2
接口适配器

实际开发过程中,有时定义的接口中包含很多方法,但是我们实际使用时,只需要一部分方法时,我们可以在我们的类和接口之间增加一个抽象类帮我们适配

即:原始接口(m1,m2,m3,m4)—>接口适配器:抽象类(实现类原始接口中的m1,m2,m3,m4)—>实际使用类,重写m1,m2

在这里插入图片描述

原始目标接口:Targetable
public interface Targetable {
    void method1();
    void method2();
}
接口适配器:InterfaceWrapper
public abstract class InterfaceWrapper implements Targetable {

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}
实际使用类1SubClassOne
public class SubClassOne extends InterfaceWrapper {
    // SubClassOne只需要method1
    @Override
    public void method1(){
        System.out.println("this is Targetable.method1 , but SubClassOne just need this");
    }
}
实际使用类2SubClassTwo
public class SubClassTwo extends InterfaceWrapper {
    // SubClassTwo只需要method2
    @Override
    public void method2(){
        System.out.println("this is Targetable.method2 , but SubClassTwo just need this");
    }
}
测试类
public class InterfaceWrapperDemoTest {
    public static void main(String[] args) {
        SubClassOne subClassOne = new SubClassOne();
        subClassOne.method1();
        System.out.println("====================================");
        SubClassTwo subClassTwo = new SubClassTwo();
        subClassTwo.method2();
    }
}
执行结果:
this is Targetable.method1 , but SubClassOne just need this
====================================
this is Targetable.method2 , but SubClassTwo just need this

总结

类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

接口的适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

优点:

  • 符合开闭原则:使用适配器而不需要改变现有类,提高类的复用性。
  • 目标类和适配器类解耦,提高程序扩展性。

缺点:

  • 增加了系统的复杂性
3.2.3 代理模式

为某个对象提供一个代理,并由这个代理对象控制对原对象的访问。可以对被代理类的进行增强

类比现实中的:中介、律师、经纪人等

在这里插入图片描述

父类接口:Source
public interface Source {
    void method();
}
被代理的实现类1SourceImpl
public class SourceImpl implements Source {
    @Override
    public void method() {
        System.out.println("this is SourceImpl.method");
    }
}
代理实现类2SourceProxy
public class SourceProxy implements Source {
    private Source source;

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

    @Override
    public void method() {
        System.out.println("this is some action before method");
        source.method();
        System.out.println("this is some action after method");
    }
}
测试类:
public class DemoTest {
    public static void main(String[] args) {
        Source source = new SourceImpl();
        SourceProxy sourceProxy = new SourceProxy(source);
        sourceProxy.method();
    }
}
执行结果:
this is some action before method
this is SourceImpl.method
this is some action after method

优点:

  • 降低系统的耦合度:代理模式能够协调调用者和被调用者,在一定程度上降低了系 统的耦合度。
  • 不同类型的代理可以对客户端对目标对象的访问进行不同的控制:
    • 远程代理,使得客户端可以访问在远程机器上的对象,远程机器 可能具有更好的计算性能与处理速度,可以快速响应并处理客户端请求。
    • 虚拟代理通过使用一个小对象来代表一个大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。
    • 保护代理可以控制客户端对真实对象的使用权限。

缺点:

  • 由于在客户端和被代理对象之间增加了代理对象,因此可能会让客户端请求的速度变慢。
3.2.4 外观模式

外观模式定义了一个高层接口,为子系统中的多组接口提供了一个统一的接口,当对外执行的时候,直接执行高层接口即可。

又称门面模式。

比如:在浏览图片时,缓存+下载都被隐藏,对外提供一个浏览接口即可。

在这里插入图片描述

下载图片:DownloadImage
public class DownloadImage {
    public void downloadImage(){
        System.out.println("this is download image method");
    }
}
缓存图片:CacheImage
public class CacheImage {
    public void cacheImage(){
        System.out.println("this is cache image method");
    }
}
对外提供的浏览图片:WebImage
public class WebImage {
    private DownloadImage downloadImage;
    private CacheImage cacheImage;

    public WebImage(){
        this.downloadImage = new DownloadImage();
        this.cacheImage = new CacheImage();
    }
    public void viewImage(){
        System.out.println("when you view image you need download and cache it");
        downloadImage.downloadImage();
        cacheImage.cacheImage();
    }
}
测试
public class DemoTest {
    public static void main(String[] args) {
        WebImage webImage = new WebImage();
        webImage.viewImage();
    }
}
执行结果:
when you view image you need download and cache it
this is download image method
this is cache image method

优点:

  • 实现了客户端与子系统间的解耦:客户端无需知道子系统的接口,简化了客户端调用子系统的调用过程,使得子系统使用起来更加容易。同时便于子系统的扩展和维护。
  • 符合迪米特法则(最少知道原则):子系统只需要将需要外部调用的接口暴露给外观类即可,而且他的接口则可以隐藏起来。

缺点:

  • 违背了开闭原则:在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的代码。
3.2.5 桥接模式

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

例如:JDBC

在这里插入图片描述

demo原型图:

在这里插入图片描述

原始接口:Phone
public interface Phone {
    void call();
}
实现类1IPhone
public class Iphone implements Phone {
    @Override
    public void call() {
        System.out.println("this is Iphone call");
    }
}
实现类2MiPhone
public class MiPhone implements Phone {
    @Override
    public void call() {
        System.out.println("this is xiaomi phone");
    }
}
桥接类:PhoneBridge
public abstract class PhoneBridge {
    private Phone phone;

    public Phone getPhone(){
        return phone;
    }
    // 动态设置实际的phone对象
    public void setPhone(Phone phone) {
        this.phone = phone;
    }

    public void call(){
       phone.call();
    }
}
对外提供的实际操作桥接类:MyPhoneBridge
public class MyPhoneBridge extends PhoneBridge {
    @Override
    public void call(){
        getPhone().call();
    }
}
测试类
public class DemoTest {
    public static void main(String[] args) {
        PhoneBridge myBridge = new MyPhoneBridge();
        myBridge.setPhone(new Iphone());
        myBridge.call();
        System.out.println("================");
        myBridge.setPhone(new MiPhone());
        myBridge.call();
    }
}
执行结果:
this is Iphone call
================
this is xiaomi phone
3.2.6 享元模式

主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用。

在这里插入图片描述

FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类。一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就拿数据库连接池来说,url、driverClassName、username、password及dbname,这些属性对于每个连接来说都是一样的,所以就适合用享元模式来处理,建一个工厂类,将上述类似属性作为内部数据,其它的作为外部数据,在方法调用时,当做参数传进来,这样就节省了空间,减少了实例的数量。

在这里插入图片描述

public class ConnectionPool {
	
	private Vector<Connection> pool;
	
	/*公有属性*/
	private String url = "jdbc:mysql://localhost:3306/test";
	private String username = "root";
	private String password = "root";
	private String driverClassName = "com.mysql.jdbc.Driver";
 
	private int poolSize = 100;
	private static ConnectionPool instance = null;
	Connection conn = null;
 
	/*构造方法,做一些初始化工作*/
	private ConnectionPool() {
		pool = new Vector<Connection>(poolSize);
 
		for (int i = 0; i < poolSize; i++) {
			try {
				Class.forName(driverClassName);
				conn = DriverManager.getConnection(url, username, password);
				pool.add(conn);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
 
	/* 返回连接到连接池 */
	public synchronized void release() {
		pool.add(conn);
	}
 
	/* 返回连接池中的一个数据库连接 */
	public synchronized Connection getConnection() {
		if (pool.size() > 0) {
			Connection conn = pool.get(0);
			pool.remove(conn);
			return conn;
		} else {
			return null;
		}
	}
}

优点:

  • 使用享元模可以减少内存中对象的数量,使得相同对象或相似对象在内存中只保存一份,降低系统的使用内存,也可以提性能。
  • 享元模式的外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享。

缺点:

  • 使用享元模式需要分离出内部状态和外部状态,这使得程序的逻辑复杂化。

  • 对象在缓冲池中的复用需要考虑线程问题。

3.3 行为型
3.3.1 职责链模式

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

or

多个对象,每个对象持有对下一个对象的引用,这样就会形成一条链,请求在这条链上传递,直到某一对象决定处理该请求。但是发出者并不清楚到底最终那个对象会处理该请求,所以,责任链模式可以实现,在隐瞒客户端的情况下,对系统进行动态的调整。

在这里插入图片描述

原始接口:Handler
public interface Handler {
    void operator();
}
中间抽象类:AbstractHandler
public abstract class AbstractHandler {
    private Handler handler;

    public Handler getHandler() {
        return handler;
    }
    public void setHandler( Handler handler){
        this.handler = handler;
    }
}
链式实现类:MyHandler
public class MyHandler extends AbstractHandler implements Handler {
    private String name;

    public MyHandler(String name){
        this.name = name;
    }
    @Override
    public void operator() {
        System.out.println("Handler is " + name);
        if (getHandler() != null){
            getHandler().operator();
        }
    }
}
测试类
public class DemoTest {
    public static void main(String[] args) {
        MyHandler handler01 = new MyHandler("handler01");
        MyHandler handler02 = new MyHandler("handler02");
        MyHandler handler03 = new MyHandler("handler03");
        // 01  --交给-->  02
        handler01.setHandler(handler02);
        // 02  --交给-->    03
        handler02.setHandler(handler03);
        handler01.operator();
    }
}
执行器:
Handler is handler01
Handler is handler02
Handler is handler03
3.3.2 命令模式

一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务(Transaction)模式。

or

命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互解耦,任何一方都不用去依赖其他人,只需要做好自己的事儿就行,司令员要的是结果,不会去关注到底士兵是怎么实现的。

在这里插入图片描述

最顶层接口:Command
public interface Command {
     void exec();
}
命令接收者(也就是实际执行的):Receiver
public class Receiver {
    public void action(){
        System.out.println("the action receive by Receiver");
    }
}
实际命令,转交给ReceiverMyCommand
public class MyCommand implements Command {
    private Receiver receiver;
    public MyCommand(Receiver receiver){
        this.receiver = receiver;
    }
    @Override
    public void exec() {
        // 命令的执行,实际由接收者去执行
        receiver.action();
    }
}
命令发出者:Invoker
public class Invoker {
    private Command command;
    public Invoker( Command command){
        this.command = command;
    }
    public void action(){
        command.exec();
    }
}
测试类
public class DemoTest {
    public static void main(String[] args) {
        // 发出命令-—->接收命令--->执行命令
        Invoker invoker = new Invoker(new MyCommand(new Receiver()));
        invoker.action();
    }
}
执行结果
the action receive by Receiver
3.3.3 解释器模式

定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。

在这里插入图片描述

3.3.4 迭代器模式

集合中非常常见,如果对集合类比较熟悉的话,理解本模式会十分轻松。这句话包含两层意思:一是需要遍历的对象,即聚集对象,二是迭代器对象,用于对聚集对象进行遍历访问。

比如:HashMap

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XK1zI9Ol-1645085395054)(png/设计模式/迭代器.png)]

3.3.5 中介者模式

用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。中介者模式又称为调停者模式,它是一种对象行为型模式。

中介者模式也是用来降低类类之间的耦合的,因为如果类类之间有依赖关系的话,不利于功能的拓展和维护,因为只要修改一个对象,其它关联的对象都得进行修改。如果使用中介者模式,只需关心和Mediator类的关系,具体类类之间的关系及调度交给Mediator就行,这有点像spring容器的作用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U5EYdIzS-1645085395055)(png/设计模式/中介者.jpg)]

User类统一接口,User1和User2分别是不同的对象,二者之间有关联,如果不采用中介者模式,则需要二者相互持有引用,这样二者的耦合度很高,为了解耦,引入了Mediator类,提供统一接口,MyMediator为其实现类,里面持有User1和User2的实例,用来实现对User1和User2的控制。这样User1和User2两个对象相互独立,他们只需要保持好和Mediator之间的关系就行,剩下的全由MyMediator类来维护!

中介接口:Mediator
public interface Mediator {
    void createMediator();
    void workAll();
}
用户类:User,需要交给Mediator去管理
public abstract class User {
    private Mediator mediator;
    public Mediator getMediator(){
        return mediator;
    }
    public User(Mediator mediator){
        this.mediator = mediator;
    }
    public abstract void work();
}
实际User对象类:User1
public class User1 extends User {
    public User1(Mediator mediator){ super(mediator); }
    @Override
    public void work() {
        System.out.println("this is user1");
    }
}
实际User对象类:User2
public class User2 extends User {
    public User2(Mediator mediator){
        super(mediator);
    }
    @Override
    public void work() {
        System.out.println("this is user2");
    }
}
实际的中介者:MyMediator
public class MyMediator implements Mediator {
    private User user1;
    private User user2;
    public User getUser1(){
        return user1;
    }
    public User getUser2(){
        return user2;
    }
    // 将创建User对象的过程交给当前类
    @Override
    public void createMediator() {
        user1 = new User1(this);
        user2 = new User2(this);
    }
  // 实际的执行也交给当前类
    @Override
    public void workAll() {
        user1.work();
        user2.work();
    }
}
测试类
public class DemoTest {
    public static void main(String[] args) {
        Mediator mediator = new MyMediator();
        mediator.createMediator();
        mediator.workAll();
    }
}
执行结果
this is user1
this is user2
3.3.6 备忘录模式

主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象,个人觉得叫备份模式更形象些,通俗的讲下:假设有原始类A,A中有各种属性,A可以决定需要备份的属性,备忘录类B是用来存储A的一些内部状态,类C呢,就是一个用来存储备忘录的,且只能存储,不能修改等操作。

在这里插入图片描述

Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value值。Memento类是备忘录类,Storage类是存储备忘录的类,持有Memento类的实例。

备份类Memmento:用来保存备份数据 
public class Memmento {
    private String value;

    public Memmento(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}
存储类 Storage :存储Memmento
public class Storage {
    private Memmento memmento;

    public Storage(Memmento memmento) {
        this.memmento = memmento;
    }

    public Memmento getMemmento() {
        return memmento;
    }

    public void setMemmento(Memmento memmento) {
        this.memmento = memmento;
    }
}
原始数据类:Original
public class Original {
    private String value;

    public Original(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
   //  创建备份
    public Memmento createMemmento(String value){
        return new Memmento(value);
    }
    // 恢复数据
    public void resetValue(Memmento memmento){
        this.value = memmento.getValue();
    }
}
测试类
public class DemoTest {
    public static void main(String[] args) {
        Original original = new Original("this is original");

        // 开始备份:创建storage
        Storage storage = new Storage(original.createMemmento(original.getValue()));

        System.out.println(original.getValue());
        original.setValue("this is changed");
        System.out.println(original.getValue());

        // 开始还原:使用storage还原
        original.resetValue(storage.getMemmento());
        System.out.println(original.getValue());
    }
}
执行结果
this is original
this is changed
this is original
3.3.7 观察者模式

定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

观察者模式很好理解,类似于邮件订阅和RSS订阅,当我们浏览一些博客或wiki时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。其实,简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。

在这里插入图片描述

3.3.8 状态模式

核心思想就是:当对象的状态改变时,同时改变其行为,很好理解!就拿QQ来说,有几种状态,在线、隐身、忙碌等,每个状态对应不同的操作,而且你的好友也能看到你的状态,所以,状态模式就两点:1、可以通过改变状态来获得不同的行为。2、你的好友能同时看到你的变化。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BiVBXvtS-1645085395056)(png/设计模式/状态模式.png)]

状态类
public class State {
    private String value;

    public State(String value) {
        this.value = value;
    }

    public State() { }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
    public void method1(){
        System.out.println("this is method1");
    }
    public void method2(){
        System.out.println("this is method2");
    }
}
内容类
public class Context {
    private State state;

    public Context(State state) {
        this.state = state;
    }

    public State getState() {
        return state;
    }

    public void setState(State state) {
        this.state = state;
    }
    public void method() {
        if (state.getValue().equals("state1")) {
            state.method1();
        } else if (state.getValue().equals("state2")) {
            state.method2();
        }
    }
}
测试类
public class DemoTest {
    public static void main(String[] args) {
        State state = new State();
        Context context = new Context(state);
        //设置第一种状态
        state.setValue("state1");
        context.method();

        //设置第二种状态
        state.setValue("state2");
 		context.method();
    }
}
执行结果
this is method1
this is method2
3.3.9 策略模式

定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化,也称为政策模式(Policy)。

策略模式定义了一系列算法,并将每个算法封装起来,使他们可以相互替换,且算法的变化不会影响到使用算法的客户。需要设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口,设计一个抽象类(可有可无,属于辅助类),提供辅助函数

在这里插入图片描述

统一接口:
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);
	}
}
3.3.10 模板方法模式

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

一个抽象类中,有一个主方法,再定义1…n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NcUIvFUO-1645085395057)(png/设计模式/模板方法.png)]

就是在AbstractCalculator类中定义一个主方法calculate,calculate()调用spilt()等,Plus和Minus分别继承AbstractCalculator类,通过对AbstractCalculator的调用实现对子类的调用

抽象类
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);
	}
}
3.3.11 访问者模式

提供一个作用于某对象结构中的各元素的操作表示,它使我们可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

简单来说,访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。

在这里插入图片描述

public interface Visitor {
	public void visit(Subject sub);
}
public class MyVisitor implements Visitor {
 
	@Override
	public void visit(Subject sub) {
		System.out.println("visit the subject:"+sub.getSubject());
	}
}
public interface Subject {
	public void accept(Visitor visitor);
	public String getSubject();
}
public class MySubject implements Subject {
 
	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}
 
	@Override
	public String getSubject() {
		return "love";
	}
}
public class Test {
 
	public static void main(String[] args) {
		
		Visitor visitor = new MyVisitor();
		Subject sub = new MySubject();
		sub.accept(visitor);	
	}
}
执行结果
visit the subject:love

参考链接:

https://www.cnblogs.com/geek6/p/3951677.html

https://juejin.cn/post/6844903475197771790#heading-43
l

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值