Skip to content

Latest commit

 

History

History
888 lines (621 loc) · 33.8 KB

常见设计模式.md

File metadata and controls

888 lines (621 loc) · 33.8 KB

设计模式不一定是要求你必须去使用它,而是通过这些设计模式让你找到“封装变化”、“对象间松散耦合”、“针对接口编程”的感觉,从而设计出易维护、易扩展、易复用、灵活性好的程序。

—出自《大话设计模式》

📃六大原则

1.单一职责原则

原话:就一个类而言,应该仅有一个引起它变化的原因

简单的理解就是:功能要单一

举个栗子:我们在编程的时候,很自然的会给一个类加各种各样的功能,比如我们写一个图书管理系统的JavaSwing应用,一般我们会生成一个窗体类,于是我们就把各种各样的代码,像某种借书还书的算法呀,像数据库访问的SQL语句呀什么的都写到这样的类中,这就意味着,无论任何需求要来,你都需要更改这个窗体类,这其实是很糟糕的,维护麻烦,复用不可能,也缺乏灵活性。

2.开闭原则

原话:软件实体(类、模块、函数等等)应该可以扩展,但是是不可以修改的。

简单的理解就是:需求千变万化,我们写代码最初就应该考虑到以后的扩展性。总不能需求改了,代码就也得全改。总之一句话,不要改以前的代码,要去加代码扩展

3.里氏代换原则

原话:所有引用父类的地方,必须能够透明的使用其子类对象。

简单的理解就是:在代码中使用父类对象的地方替换成子类对象没事,反过来就不行。像刚才提到的开闭原则的重要实现方式之一就是这个里氏代换原则。我们尽量使用父类类型定义对象,运行时再确定子类类型,用子类对象来替换父类对象

需要注意的地方:子类中所有的方法必须得在父类中声明,换言之,子类必须实现父类声明的所有方法。这里**尽量把父类声明为抽象类、接口,使子类继承父类或实现父类接口,并实现父类中声明的方法。**在运行时,子类实例替换父类实例,既方便功能扩展,又不至于对源代码大张旗鼓的改动。若新增一个功能,就新增一个子类实现即可。

4.依赖倒置原则

原话:高层模块不应依赖低层模块,他们都应该依赖抽象。抽象不依赖细节,细节依赖抽象。

简单的理解就是:面向接口编程,依赖于抽象而不依赖于具体。

举个栗子:请求网络既可以用okhttp,也可以用原生API,只需要封装一个网络请求接口,创建他俩的接口实现类就行了。

5.接口隔离原则

原话:一个类对另一个类的依赖,应该建立在最小的接口上。

简单的理解就是:**接口中的方法一定要少,切忌接口臃肿。尽量使用多个隔离的接口,比使用单个接口要好。**这个模式强调降低依赖,降低耦合。

举个栗子:我现在有一家书店进行书籍的售卖,卖童话书,卖计算机的书。如果我把卖的这些书都方法放在一个接口里,那如果有更多种类的书,那么这个接口就会变的十分臃肿,所以可以将这些卖不同书的分成一个单独的接口。

6.迪米特法则

原话:一个软件实体,应尽可能少的与其它实体发生作用。

简单的理解就是:这个法则其实就是最少知道原则,即一个模块发生修改时,应该尽可能少的减少对象间的交互和影响其它模块,使得系统功能模块相对独立。

举个栗子:老师想从学生那里知道假期一共留了几门作业,所以Teacher->StudentA 是直接关系,老师调用学生,StudentA->homework 是直接关系,但老师和课程Teacher->homework 没有直接关系,所以依据迪米特最少知道原则,Teacher类中不能出现homework类的信息

💕常用设计模式

单例模式

单例模式目的是为了一个类只有一个实例。 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 优点:

  1. 内存中只有一个实例,减少了内存开销;
  2. 可以避免对资源的多重占用;
  3. 设置全局访问点,严格控制访问。

缺点: 1.没有接口,拓展困难。 使用场景:

  1. 要求生产唯一序列号。
  2. WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  3. 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。 注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

单例模式跟创建一个全局变量有啥区别

单例模式与创建一个全局变量在目的和效果上存在一定的相似性,即都旨在提供一种全局访问的机制,但它们在实现方式、控制粒度、灵活性和线程安全性等方面存在显著的区别。

区别归纳

  1. 实现方式
    • 全局变量:全局变量是在程序的所有部分都可以直接访问的变量,通常定义在函数或类的外部。全局变量的生命周期贯穿整个程序运行期间,一旦被创建就会一直占用内存资源。
    • 单例模式:单例模式是一种设计模式,通过封装类的实例化过程来确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。单例模式的实现通常包括私有化构造函数、提供一个静态的私有变量来保存类的唯一实例,以及一个公开的静态方法来获取这个实例。
  2. 控制粒度
    • 全局变量:全局变量的控制粒度较粗,因为它在整个程序中都是可见的,这可能导致不同部分的代码之间产生不必要的耦合,降低代码的可维护性和可测试性。
    • 单例模式:单例模式提供了更细粒度的控制,因为它将实例的创建和访问封装在类内部,其他类需要通过特定的方法来获取实例。这种方式有助于减少代码的耦合度,提高代码的可维护性和可测试性。
  3. 灵活性
    • 全局变量:全局变量的灵活性相对较低,因为它一旦定义就很难更改其类型或作用域。此外,全局变量的命名也可能与其他变量发生冲突。
    • 单例模式:单例模式相对更加灵活,因为它允许通过继承等方式进行扩展。此外,单例模式还可以提供懒加载(即延迟实例化)等特性,以满足不同的需求。
  4. 线程安全性
    • 全局变量:全局变量在多线程环境下通常不是线程安全的,因为它们可以被多个线程同时访问和修改,这可能导致数据竞争和一致性问题。
    • 单例模式:单例模式可以通过适当的同步机制(如使用synchronized关键字或锁)来确保在多线程环境下的线程安全性。这意味着单例类的实例在多线程环境中仍然可以保持唯一性和数据一致性。
  5. 封装性
    • 全局变量:全局变量的封装性较差,因为它们直接暴露给程序的所有部分,这可能导致对全局变量的不当修改和滥用。
    • 单例模式:单例模式通过将实例的创建和访问封装在类内部,提供了更好的封装性。这种方式有助于隐藏类的内部实现细节,减少外部对类的依赖和干扰。

综上所述,单例模式和全局变量虽然都提供了全局访问的机制,但它们在实现方式、控制粒度、灵活性和线程安全性等方面存在显著的区别。在实际开发中,应根据具体需求和场景选择合适的方式来实现全局访问。如果需要更好的封装性、控制粒度、灵活性和线程安全性,可以考虑使用单例模式;如果需求相对简单且对性能要求不高,则可以考虑使用全局变量。但需要注意的是,过多地使用全局变量可能会导致代码难以维护和理解,因此应谨慎使用。

懒汉模式,线程不安全

懒汉模式下的单例写法是最简单的,但它是线程不安全的!

懒汉模式,线程安全

加同步锁解决线程安全问题:

但是同步锁锁的是整个类,比较消耗资源,并且即使运行内存中已经存在LazySingleton,调用其getInstance还是会上锁,所以这种写法也不是很好。

饿汉式

“饿汉”意指在类加载的时候就初始化:这种方式比较常用,但容易产生垃圾对象。

但由于不是懒加载,饿汉模式不管需不需要用到实例都要去创建实例,如果创建了不使用,则会造成内存浪费。

优点:没有加锁,执行效率会提高。

缺点:类加载时就初始化,浪费内存。

双重校验锁单例模式(DCL,即double-check-lock)

instance属性上加上volatile关键字的原因:如果只是加了同步锁,它还是线程不安全的,虽然不会出现多次初始化LazyDoubleCheckSingleton实例的情况,但是由于指令重排的原因,某些线程可能会获取到空对象,后续对该对象的操作将触发空指针异常。

静态内部类单例模式

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

在Java规范中,当以下这些情况首次发生时,A类将会立刻被初始化:

\1. A类型实例被创建;

\2. A类中声明的静态方法被调用;

\3. A类中的静态成员变量被赋值;

\4. A类中的静态成员被使用(非常量);

枚举

这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。

它更简洁,自动支持序列化机制,绝对防止多次实例化。

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

枚举的最终源码是有参构造!并不是所谓所说的默认无参构造!

重要经验

经验之谈:一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。

工厂模式

工厂模式的目的是实现不同条件下创建不同实例

工厂模式是Java中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式

在_简单工厂模式_中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口指向新创建的对象

优点:

  1. 一个调用者想创建一个对象,只要知道其名称就行了。
  2. 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
  3. 屏蔽产品的具体实现,调用者只关心产品的接口

缺点:

  1. 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统具体类的依赖。这并不是好事。

使用场景:

  1. 日志记录器:记录可能记录到本地磁盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
  2. 数据库访问:当用户不知道最后系统采用了哪一类的数据库,以及数据库可能有变化时
  3. 设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。

注意事项:作为一种创建类模式,在任何需要生成_复杂*_对象的地方,都可以使用工厂方法模式*。而简单对象,特别是只需要new就可以完成创建的对象,无需使用工厂模式,因为会增加系统的复杂度。

实现方式

  • 创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory
  • FactoryPatternDemo,我们的演示类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。
  1. 创建一个Shape接口。
  2. 创建接口的实现类
  3. 创建一个工厂,生成基于给定信息的实体类的对象。
  4. 使用该工厂,通过传递类型信息来获取实体类的对象。
  5. 验证输出

原型模式

原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。

举个栗子理解一下:一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要它的时候更新数据库,以此来减少数据库调用。

原型模式就像复印机,可以复印多份相同的;像孙悟空的猴毛,拔下一吹就变出很多个孙悟空一样简单快捷。

优点:

  1. 性能提高了
  2. 减少构造方法的调用

缺点:

  1. 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低
  2. 总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活。

使用场景:

  1. 资源优化场景
  2. 类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等
  3. 性能和安全要求的场景
  4. 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式
  5. 一个对象多个修改者的场景
  6. 一个对象需要提供给其它对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用
  7. 在实际项目中,原型模式很少单独出现,一般是和_工厂方法_模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与Java融为一体,可以随手拿来用。

注意事项:

  • 与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。- 浅拷贝实现Cloneable接口,重写clone()方法- 深拷贝是通过实现Serializable读取二进制流什么是深浅拷贝?
  • 浅拷贝:创建一个新对象(副本),然后将非静态属性复制到新对象。如果属性是值类型,则对该属性执行逐位复制。如果属性是引用类型,则复制引用,但不复制引用的对象;因此,原始对像与副本引用同一个对象。
  • 深拷贝:创建一个新对象(副本),属性是引用类型,引用的对象也会被克隆,不再指向原有对象地址

实现方式我们将创建一个抽象类 Fruit 和扩展了 Fruit 类的实体类。下一步是定义类 FruitCache,该类把 fruit 对象存储在一个Hashtable 中,并在请求的时候返回它们的克隆。PrototypPatternDemo,我们的演示类使用 FruitCache 类来获取 Fruit 对象。

  1. 创建一个实现了Cloneable接口的抽象类
  2. 创建扩展了上面抽象类的实体类
  3. 创建一个类,把他们存储在一个HashTable中
  4. 使用FruitCache类获取存储在HashTable中的类型的克隆。
  5. 验证输出

代理模式

在代理模式中,一个类代表另一个类的功能。这种类型的设计属于结构型模式。

我们创建具有现有对象的对象,以便向外界提供功能接口。

这个模式为其他对象提供了一种代理以控制对这个对象的访问

优点:

  1. 职责清晰
  2. 高扩展性
  3. 智能化

缺点:

  1. 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
  2. 实现代理模式需要额外的工作,有些代理模式的实现非常复杂

使用场景:

  1. 当我们想隐藏某个类时,可以为其提供代理类
  2. 当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现(代理类不一定只有一个,我们可以建立多个代理类来实现,也可以在一个代理类中进行权限判断来进行不同权限的功能调用)
  3. 当我们要扩展某个类的某个功能时,可以使用代理模式,在代理类中进行简单扩展。

注意事项:

  1. 和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。

实现方式

我们将创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。

ProxyPatternDemo,我们的演示类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示。

  1. 创建一个接口
  2. 创建接口的实体类
  3. 创建代理类实现接口
  4. 当被请求时,使用 ProxyImage 来获取 RealImage 类的对象。
  5. 验证输出

适配器模式

适配器模式是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。

这种模式涉及到一个单一的类,该类负责加入独立的或不兼容的接口功能。举个真实的例子,读卡器是作为内存卡和笔记本之间的适配器。将内存卡插入读卡器,再将读卡器插入笔记本,这样就可以通过笔记本来读取内存卡。

这种模式是将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。其别名为包装器。

优点:

  1. 可以让任何两个没有关联的类一起运行
  2. 提高了类的复用
  3. 增加了类的透明度
  4. 灵活性好

缺点:

  1. 过多地使用适配器,会让系统非常零乱,不易整体把握。比如,明明调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
  2. 由于Java至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类

使用场景:

有动机地修改一个正常运行的接口,这时应该考虑使用适配器模式。

注意事项:适配器不是在详细设计时添加的,而是解决正在服役的项目的问题。

实现方式

  1. 创建一个源角色
  2. 创建目标角色
  3. 创建适配器角色
  4. 客户端调用

策略模式

在策略模式中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

策略模式:定义一系列算法,并将每个算法封装起来,使他们可以互相替换,且算法的变化不会影响使用算法的客户。

使用了策略模式,在系统设计层面是满足里氏替换原则开闭原则的,每个算法可以相互替代,在不修改已有算法的情况下易于扩展。

这个设计模式主要解决了在有多种算法相似的情况下,使用if...else...所带来的复杂和难以维护。

查看 Spring 某些源码,也会看到有多层 if…else…嵌套的的使用。if…else… 在一些分支判断处理上是简单直接明了的。稍有个1年以上开发经验都应该不会写一大堆的嵌套 if…else….(超过三层的嵌套),最简单的优化处理是抽出为独立的方法。

要使用设计模式的重点是关注可扩展,可维护。使用策略模式替换 if…else… 的核心是各个算法独立,可以相互替换互不影响。

优点:

  1. 算法可以自由切换
  2. 避免使用多重条件判断
  3. 扩展性良好

缺点:

  1. 策略类会增多
  2. 所有策略类都需要对外暴露

使用场景:

1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。

2、一个系统需要动态地在几种算法中选择一种。

3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。

实现方式

我们将创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。

StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。

  1. 创建一个接口
  2. 创建实现接口的实体类。
  3. 创建 Context 类。
  4. 使用 Context 来查看当它改变策略 Strategy 时的行为变化。
  5. 验证输出
public class Homework {
}
public class Teacher {
    public void checkHomework(StudentA studentA){
        studentA.checkHomeworkSum();
    }
}
public class StudentA {
    private List<Homework> arrayList = new ArrayList<>();
    public void checkHomeworkSum(){
        for (int i = 0; i < 5; i++) {
            arrayList.add(new Homework());
        }
        System.out.println("假期一共留了多少门作业:" + arrayList.size() + "门");
    }
}
public class DemeterTest {
    public static void main(String[] args) {
        Teacher teacher = new Teacher();
        StudentA studentA = new StudentA();
        teacher.checkHomework(studentA);
    }
}
public class LazySingleton{
    private static LazySingleton lazySingleton = null;
    private LazySingleton(){


    }
    public static LazySingleton getInstance(){
        if(lazySingleton == null){
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}
public class LazySingleton {


    private static LazySingleton lazySingleton = null;


    private LazySingleton() {


    }


    public static LazySingleton getInstance() {
        synchronized (LazySingleton.class) {
            if (lazySingleton == null) {
                lazySingleton = new LazySingleton();
            }
        }
        return lazySingleton;
    }
}
public class LazySingleton {


    private static LazySingleton lazySingleton = null;


    private LazySingleton() {


    }


    public static LazySingleton getInstance() {
        synchronized (LazySingleton.class) {
            if (lazySingleton == null) {
                lazySingleton = new LazySingleton();
            }
        }
        return lazySingleton;
    }
public class LazyDoubleCheckSingleton{
    private volatile static LazyDoubleCheckSingleton instance = null;
    private LazyDoubleCheck(){}


    public static LazyDoubleCheckSingleton getInstance(){
        if(instance == null){
            synchronized(LazyDoubleCheckSingleton.class){
                if(instance == null){
                    instance = new LazyDoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}
public class StaticInnerClassSingleton{
    private StaticInnerClassSingleton(){}


    private static class InnerClass{
        private static StaticInnerClassSingleton instance = new StaticInnerClassSingleton();
    }
    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.instance;
    }
}
public enum EnumSingleton{
    INSTANCE;


    public static EnumSingleton getInstance(){
        return INSTANCE;
    }
}
interface Shape{  
    void draw();  
}
class Rectangle implements Shape{  
  
    @Override  
    public void draw() {  
        System.out.println("Inside Rectangle::draw() method.");  
    }  
}  
  
class Square implements Shape{  
  
    @Override  
    public void draw() {  
        System.out.println("Inside Square::draw() method.");  
    }  
}  
  
class Circle implements Shape{  
  
    @Override  
    public void draw() {  
        System.out.println("Inside Circle::draw() method.");  
    }  
}
class ShapeFactory{  
  
    //使用getShape方法获取形状类型的对象  
    public Shape getShape(String shapeType){  
        if (shapeType == null){  
            return null;  
        }  
        if (shapeType.equalsIgnoreCase("CIRCLE")){  
            return new Circle();  
        }else if(shapeType.equalsIgnoreCase("RECTANGLE")){  
            return new Rectangle();  
        }else if(shapeType.equalsIgnoreCase("SQUARE")){  
            return new Square();  
        }  
        return null;  
    }  
  
}
public class FactoryPatternDemo {


   public static void main(String[] args) {
      ShapeFactory shapeFactory = new ShapeFactory();


      //获取 Circle 的对象,并调用它的 draw 方法
      Shape shape1 = shapeFactory.getShape("CIRCLE");


      //调用 Circle 的 draw 方法
      shape1.draw();


      //获取 Rectangle 的对象,并调用它的 draw 方法
      Shape shape2 = shapeFactory.getShape("RECTANGLE");


      //调用 Rectangle 的 draw 方法
      shape2.draw();


      //获取 Square 的对象,并调用它的 draw 方法
      Shape shape3 = shapeFactory.getShape("SQUARE");


      //调用 Square 的 draw 方法
      shape3.draw();
	   }
   }
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
abstract class Fruit implements Cloneable{  
  
    private String id;  
  
    protected String type;  
  
    abstract void draw();  
  
    public String getId() {  
        return id;  
    }  
  
    public void setId(String id) {  
        this.id = id;  
    }  
  
    public String getType() {  
        return type;  
    }  
  
    @Override  
    protected Object clone() {  
        Object clone = null;  
        try {  
            clone = super.clone();  
        } catch (CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
        return clone;  
    }  
}
class Apple extends Fruit{  
    public Apple(){  
        type = "Apple";  
    }  
  
    @Override  
    void draw() {  
        System.out.println("draw apple");  
    }  
}  
  
class Orange extends Fruit{  
    public Orange(){  
        type = "Orange";  
    }  
  
    @Override  
    void draw() {  
        System.out.println("draw orange");  
    }  
}
class FruitCache{  
    private static Hashtable<String,Fruit> fruitMap = new Hashtable<>();  
    public static Fruit getFruit(String fruitId) {  
        Fruit cacheFruit = fruitMap.get(fruitId);  
        return (Fruit)cacheFruit.clone();  
    }  
    //对每种水果都运行数据库查询,并创建该水果  
    // fruitMap.put(fruitId,fruit);  
    public static void loadCache(){  
        Apple apple = new Apple();  
        apple.setId("1");  
        fruitMap.put(apple.getId(),apple);  
  
        Orange orange = new Orange();  
        orange.setId("2");  
        fruitMap.put(orange.getId(),orange);  
    }  
}
@Test  
public void prototypeDemo(){  
    FruitCache.loadCache();  
  
    System.out.println(FruitCache.getFruit("1").getType());  
  
    System.out.println(FruitCache.getFruit("2").getType());  
}
Apple
Orange
interface Image{  
    void display();  
}
class RealImage implements Image{  
    private String fileName;  
  
    @Override  
    public void display() {  
        System.out.println("Displaying "+fileName);  
    }  
  
    private void loadFromDisk(String fileName){  
        System.out.println("Loading "+fileName);  
    }  
  
    public RealImage(String fileName){  
        this.fileName = fileName;  
        loadFromDisk(fileName);  
    }  
}
class ProxyImage implements Image{  
  
    private RealImage realImage;  
    private String fileName;  
  
    public ProxyImage(String fileName){  
        this.fileName = fileName;  
    }  
  
    @Override  
    public void display() {  
        if (realImage == null){  
            realImage = new RealImage(fileName);  
        }  
        realImage.display();  
    }  
}
@Test  
public void proxyDemo(){  
    ProxyImage image = new ProxyImage("test1.jpg");  
    //图像将从磁盘加载  
    image.display();  
    System.out.println("");  
    //图像将无法从磁盘加载  
    image.display();  
}
Loading test1.jpg
Displaying test1.jpg


Displaying test1.jpg
public abstract class Adaptee {  
    public void specificRequest(){  
        System.out.println("specificRequest");  
    }  
}
public interface Target {  
    void request();  
}
public class Adapter implements Target {  
  
    private Adaptee adaptee;  
  
    public Adapter(Adaptee adaptee) {  
        this.adaptee = adaptee;  
    }  
  
    @Override  
    public void request() {  
        adaptee.specificRequest();  
    }  
}
public class Client {  
  
    public static void main(String[] args) {  
        Adaptee adaptee = new Adaptee();  
        Target target = new Adapter(adaptee);  
        target.request();  
    }  
}
public class OperationAdd implements Strategy{
   @Override
   public int doOperation(int num1, int num2) {
      return num1 + num2;
   }}
class OperationAdd implements Strategy{  
    @Override  
    public int doOperation(int num1, int num2) {  
        return num1+num2;  
    }  
}  
  
class OperationSubstract  implements Strategy{  
    @Override  
    public int doOperation(int num1, int num2) {  
        return num1-num2;  
    }  
}  
  
class OperationMultiply implements Strategy{  
    @Override  
    public int doOperation(int num1, int num2) {  
        return num1 * num2;  
    }  
}
public class Context {
   private Strategy strategy;


   public Context(Strategy strategy){
      this.strategy = strategy;
	}


   public int executeStrategy(int num1, int num2){
      return strategy.doOperation(num1, num2);
	}
}
@Test  
public void strategyDemo(){  
    Context context = new Context(new OperationAdd());  
    System.out.println("10 + 5 = "+context.executeStrategy(10,5));  
  
    context = new Context(new OperationSubstract());  
    System.out.println("10 - 5 = "+context.executeStrategy(10,5));  
  
    context = new Context(new OperationMultiply());  
    System.out.println("10 * 5 = "+context.executeStrategy(10,5));  
}
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50

Plain Text

源自 Thoughts 文档,点击注册

常见设计模式

📃六大原则

1.单一职责原则

2.开闭原则

3.里氏代换原则

4.依赖倒置原则

5.接口隔离原则

6.迪米特法则

💕常用设计模式

单例模式

1.懒汉模式,线程不安全

2.懒汉模式,线程安全

3.饿汉式

4.双重校验锁单例模式(DCL,即double-check-lock)

5.静态内部类单例模式

6.枚举

重要经验

工厂模式

原型模式

代理模式

适配器模式

策略模式