一、什么是设计模式

  • 设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。

  • 1995年,GoF(Gang of Four,四人组/四人帮)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了23种设计模式,从此树立了软件设计模式领域的里程碑,人称「GoF设计模式」。

1、设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。

正确使用设计模式具有以下优点:

  • 可以提高程序员的思维能力、编程能力和设计能力。
  • 使程序设计更加标准化、代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
  • 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

  • 设计模式分类

    创建型模式:
    单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式。结构型模式:

    结构型模式:
    适配器模式,桥接模式,装饰模式,组合模式,外观模式,享元模式,代理模式:

    行为型模式:
    模板方法模式,命令模式,迭代器模式,观察者模式,中介者模式,备忘录模式,解释器
    模式,状态模式,策略模式,职责链模式,访问者模式。

二、OOP七大原则

  • 开闭原则: 对扩展开放,对修改关闭 ()
    • 里氏替换原则: 继承必须确保超类所拥有的性质在子类中仍然成立
    • 依赖倒置原则: 要面向接口编程,不要面向实现编程。
    • 单一职责原则: 控制类的粒度大小、将对象解耦、提高其内聚性。
    • 接口隔离原则: 要为各个类建立它们需要的专用接口
    • 迪米特法则: 只与你的直接朋友交谈,不跟“陌生人”说话。
    • 合成复用原则: 尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

三、单例模式

  1. 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。<br /> 这种模式涉及到一个单一的类,该类负责**创建自己的对象**,同时确保**只有单个对象**被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。<br />**注意:**<br />1、单例类只能有一个实例。<br />2、单例类必须自己创建自己的唯一实例。<br />3、单例类必须给所有其他对象提供这一实例。

主要解决:一个全局使用的类频繁地创建与销毁。

  1. 何时使用:当您想控制实例数目,节省系统资源的时候。
  2. 如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

关键代码:构造函数是私有的。

  1. 优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
  2. 2、避免对资源的多重占用(比如写文件操作)。
  3. 缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

单例模式的应用场景:

1.在应用场景中,某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。

2.当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。

3.当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
[

](https://blog.csdn.net/qq594913801/article/details/85248286)

1.饿汉式单例

  1. //饿汉单列模式
  2. public class HungrySingle {
  3. private HungrySingle(){
  4. }
  5. private final static HungrySingle single=new HungrySingle();
  6. public static HungrySingle getInstance(){
  7. return single;
  8. }
  9. }

总结:

  • 适用于单例模式较少的场景
  • 如果我们在程序启动后,一定会加载到类,那么用饿汉模式实现的单例简单又实用;
  • 如果我们是写一些工具类,则优先考虑使用懒汉模式,可以避免提前被加载到内存中,占用系统资源

2.懒汉式

  1. //懒汉单列模式
  2. public class LazySingle {
  3. private LazySingle(){
  4. System.out.println(Thread.currentThread().getName()+"ok");
  5. }
  6. private static LazySingle lazySingle;
  7. public static LazySingle getLazySingle(){
  8. if (lazySingle==null){
  9. lazySingle=new LazySingle();
  10. }
  11. return lazySingle;
  12. }

总结:
1.在实例化对象时会进行判断对象是否已实例,有则直接返回,没有则实例化对象。
2.不适合多线程场景,会出现重复实例对象。

2.1懒汉式 -DCL双重校验锁

  1. 综合了懒汉式和饿汉式两者的优缺点整合而成。<br /> 看上面代码实现中,特点是在synchronized关键字内外都加了一层 if 条件判断,<br /> 这样既保证了线程安全,又比直接上锁提高了执行效率,还节省了内存空间
  1. public class LazySingle2 {
  2. private LazySingle2(){
  3. }
  4. //这里有一个安全隐患 编译器的重排序
  5. //1分配内存空间---》2初始化对象---》3将对象指向刚分配的内存空间
  6. //但是有些编译器为了性能的原因,可能会将第二步和第三步进行重排序,顺序就成了:
  7. //1分配内存空间---》2将对象指向刚分配的内存空间---》3初始化对象
  8. //解决方法:加入volatile关键字 重排序被禁止
  9. private static volatile LazySingle2 single2;
  10. public static LazySingle2 getInstance(){
  11. if (single2 == null){
  12. synchronized (LazySingle2.class){
  13. if (single2 == null){
  14. single2=new LazySingle2();
  15. }
  16. }
  17. }
  18. return single2;
  19. }

总结:
1.加入两个if判断,1.检查变量是否被初始化(不去获得锁),如果已被初始化直接返回。
2.获取锁
3.再次检查变量是否已经被初始化,如果还没被初始化就初始化一个对象。
执行双重检查是因为,如果多个线程同时了通过了第一次检查,并且其中一个线程首先通过了第二次检查并实例化了对象,那么剩余通过了第一次检查的线程就不会再去实例化对象。

由于编译器的重排序,存在下面的隐患:

Time Thread A Thread B
T1 检查到uniqueSingleton为空
T2 获取锁
T3 再次检查到uniqueSingleton为空
T4 为uniqueSingleton分配内存空间
T5 将uniqueSingleton指向内存空间
T6 检查到uniqueSingleton不为空
T7 访问uniqueSingleton(此时对象还未完成初始化)
T8 初始化uniqueSingleton

在这种情况下,T7时刻线程B对uniqueSingleton的访问,访问的是一个初始化未完成的对象。
解决办法:为了解决上述问题,需要在uniqueSingleton前加入关键字volatile。使用了volatile关键字后,重排序被禁止,所有的写(write)操作都将发生在读(read)操作之前。

通过暴力反射可以破坏单例模式规则

  1. public static void main(String[] args)
  2. throws Exception {
  3. //反射会破坏规则
  4. // LazySingle2 lazySingle = LazySingle2.getInstance();
  5. //获取空参构造
  6. Field flag = LazySingle2.class.getDeclaredField("flag");
  7. flag.setAccessible(true);
  8. Constructor<LazySingle2> constructor = LazySingle2.class.getDeclaredConstructor(null);
  9. Constructor<LazySingle2> constructor2 = LazySingle2.class.getDeclaredConstructor(null);
  10. //开启暴力反射
  11. constructor.setAccessible(true);
  12. constructor2.setAccessible(true);
  13. LazySingle2 lazySingle2 = constructor.newInstance();
  14. flag.set(lazySingle2,false);
  15. LazySingle2 lazySingle3 = constructor2.newInstance();
  16. System.out.println(lazySingle3);
  17. System.out.println(lazySingle2);
  18. }

3.静态内部类

  1. /**
  2. *
  3. * @ClassName: Singleton4
  4. * @Description: 静态(类级)内部类:这种方式能达到双检锁方式一样的功效,但实现更简单。
  5. * 对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。
  6. * 这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
  7. * @author: ljx
  8. * @date: 2018年12月25日 下午4:05:11
  9. */
  10. class Singleton4{
  11. private Singleton4(){};
  12. /**
  13. *
  14. * @ClassName: Singleton4Holder
  15. * @Description: 类级内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
  16. * 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
  17. * @author: ljx
  18. * @date: 2018年12月25日 下午4:04:03
  19. */
  20. private static class Singleton4Holder{
  21. //静态初始化器,由JVM来保证线程安全
  22. private static Singleton4 singleton4 = new Singleton4();
  23. }
  24. public static Singleton4 getSingleton4(){
  25. return Singleton4Holder.singleton4;
  26. }
  27. }

4.枚举单例

  1. /**
  2. * @author chenGen
  3. * @version 1.0.0
  4. * @ClassName EnumSingle.java
  5. * @Description TODO
  6. * @createTime 2021年10月08日 13:37:00
  7. */
  8. //枚举单例
  9. public enum EnumSingle {
  10. INSTANCE;
  11. private String name;
  12. public String getName() {
  13. return name;
  14. }
  15. public void setName(String name) {
  16. this.name = name;
  17. }
  18. public EnumSingle getInstance(){
  19. return EnumSingle.INSTANCE;
  20. }
  21. }
  22. class Test{
  23. public static void main(String[] args)
  24. throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
  25. // EnumSingle instance1 = EnumSingle.INSTANCE;
  26. // Constructor<EnumSingle> constructor = EnumSingle.class.getDeclaredConstructor(null);
  27. // constructor.setAccessible(true);
  28. //
  29. // EnumSingle instance2 = constructor.newInstance();
  30. EnumSingle instance = EnumSingle.INSTANCE;
  31. instance.setName("单列1");
  32. System.out.println(instance.getName());
  33. EnumSingle instance2 = EnumSingle.INSTANCE;
  34. instance2.setName("单列2");
  35. System.out.println(instance2.getName());
  36. System.out.println(instance.getName()+instance2.getName());
  37. }
  38. }

总结:
1.无法被反射破坏
java规范字规定,每个枚举类型及其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定。在序列化的时候Java仅仅是将枚举对象的name属性输到结果中,反序列化的时候则是通过java.lang.Enum的valueOf()方法来根据名字查找枚举对象。也就是说,序列化的时候只将EnumSingle这个名称输出,反序列化的时候再通过这个名称,查找对应的枚举类型,因此反序列化后的实例也会和之前被序列化的对象实例相同。
[

](https://blog.csdn.net/qq_38844728/article/details/88903939)

三、工厂模式

三种模式:简单工厂模式,工厂方法模式,抽象工厂模式。

简单工厂模式:

  1. /**
  2. * @author chenGen
  3. * @version 1.0.0
  4. * @ClassName Car.java
  5. * @Description TODO
  6. * @createTime 2021年10月08日 16:44:00
  7. */
  8. //车接口
  9. public interface Car {
  10. void name();
  11. }
  1. /**
  2. * @author chenGen
  3. * @version 1.0.0
  4. * @ClassName CarFactory.java
  5. * @Description TODO
  6. * @createTime 2021年10月08日 16:49:00
  7. */
  8. //简单工厂模式
  9. public class CarFactory {
  10. public static Car getCar(String name) {
  11. if (name.equals("Tesla")){
  12. return new TeslaCar();
  13. }else if (name.equals("WuLin")){
  14. return new WuLinCar();
  15. }else {
  16. return null;
  17. }
  18. }
  19. }
  1. public static void main(String[] args) {
  2. //传统方法 获取每一辆车的实例
  3. Car car=new WuLinCar();
  4. Car car2=new TeslaCar();
  5. car.name();
  6. car2.name();
  7. System.out.println("-----------------");
  8. //工厂方法模式
  9. Car tesla = CarFactory.getCar("Tesla");
  10. Car wuLin = CarFactory.getCar("WuLin");
  11. tesla.name();
  12. wuLin.name();
  13. }
  14. }

总结:
核心本质:实例化对象不使用new,使用工厂方法代替。
将选择实现类,创建对象统一管理和控制,从而将调用者跟我们实现类解耦。

缺点:
如果需要新增加对象,需要对工厂进行修改,不符合开闭原则。(对修改关闭,允许扩展)

image.png

工厂方法模式

  1. //车接口
  2. public interface Car {
  3. void name();
  4. }
  5. public interface CarFactory {
  6. Car getCar();
  7. }
  8. public class TeslaCar implements Car {
  9. @Override
  10. public void name() {
  11. System.out.println("特斯拉汽车!");
  12. }
  13. }
  14. public class TeslaCarFactory implements CarFactory{
  15. @Override
  16. public Car getCar() {
  17. return new TeslaCar();
  18. }
  19. }
  20. //工厂方法模式
  21. public class Consumer {
  22. public static void main(String[] args) {
  23. Car car = new TeslaCarFactory().getCar();
  24. Car car1 = new WuLinCarFactory().getCar();
  25. car.name();
  26. car1.name();
  27. }
  28. }

总结:符合了开闭原则,相对于简单工厂,在上面又增加了一层车工厂。如需增加车,就要增加相对应的车工厂和实体。
缺点:不易于维护,代码冗余增加。
image.png

抽象工厂模式:

image.png

定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类。

适用场景:

  • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
  • 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
  • 提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现

优点:

  • 具体产品在应用层的代码隔离,无需关心创建的细节
  • 将一个系列的产品统一到一起创建

缺点:

  • 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
  • 增加了系统的抽象性和理解难度 ```java //抽象产品工厂 public interface IProductFactory { IphoneProduct iphoneProduct();

    IRouterProduct irouterProduct(); }

public class HuaweiFactory implements IProductFactory{

@Override public IphoneProduct iphoneProduct() { return new HuaweiPhone(); }

@Override public IRouterProduct irouterProduct() { return new HuaweiRouter(); } }

  1. <a name="LkD4d"></a>
  2. ## 建造者模式:
  3. - 建造者模式也属于创建型模式它提供了一种创建对象的最佳方式。
  4. - 定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
  5. - 主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
  6. - 用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
  7. - 例子:
  8. - 工厂(建造者模式)︰负责制造汽车(组装过>程和细节在工厂内)
  9. - 汽车购买者(用户)︰你只需要说出你需要的>型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的(车轮、车门、>发动机、方向盘等等))
  10. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/22416087/1638167179819-8674a5d2-3c67-4186-9ee3-043cfb5602e3.png#clientId=u8bae7aae-bd5f-4&from=paste&height=309&id=u3cf12160&margin=%5Bobject%20Object%5D&name=image.png&originHeight=617&originWidth=1246&originalType=binary&ratio=1&size=81113&status=done&style=none&taskId=u0faf364b-c380-4ab7-a581-979019d34c9&width=623)
  11. 代码:<br />对应抽象Builder
  12. ```java
  13. //抽象建造者:提供方法
  14. abstract class Builder {
  15. abstract void BuilderA();
  16. abstract void BuilderB();
  17. abstract void BuilderC();
  18. abstract void BuilderD();
  19. abstract Product getProduct();
  20. }

具体抽象实现,负责创建产品

  1. //工人:负责构建房子的具体 继承Builder
  2. public class Worker extends Builder{
  3. //房子产品
  4. private Product product;
  5. public Worker() {
  6. //空参创建房子
  7. product=new Product();
  8. }
  9. @Override
  10. void BuilderA() {
  11. product.setBuilderA("地基");
  12. System.out.println("地基");
  13. }
  14. @Override
  15. void BuilderB() {
  16. product.setBuilderB("钢筋混凝土");
  17. System.out.println("钢筋混凝土");
  18. }
  19. @Override
  20. void BuilderC() {
  21. product.setBuilderC("铺电线");
  22. System.out.println("铺电线");
  23. }
  24. @Override
  25. void BuilderD() {
  26. product.setBuilderD("刷粉");
  27. System.out.println("刷粉");
  28. }
  29. @Override
  30. Product getProduct() {
  31. //直接返回产品
  32. return product;
  33. }
  34. }

指挥者:负责构建过程

  1. //包工头:负责指挥工人创建什么样的房子,
  2. public class Contractor {
  3. //参数是工人 多态
  4. Product BuilderProduct(Builder worker){
  5. worker.BuilderA();
  6. worker.BuilderB();
  7. worker.BuilderC();
  8. worker.BuilderD();
  9. return worker.getProduct();
  10. }
  11. }

测试:

  1. public class Test {
  2. public static void main(String[] args) {
  3. Contractor contractor=new Contractor();
  4. Product product = contractor.BuilderProduct(new Worker());
  5. System.out.println(product.toString());
  6. }
  7. }

原型模式

  1. package com.chen.prototype;
  2. import java.util.Date;
  3. /**
  4. * @author chenGen
  5. * @version 1.0.0
  6. * @ClassName Video.java
  7. * @Description TODO
  8. * @createTime 2021年11月29日 15:09:00
  9. */
  10. public class Video implements Cloneable {
  11. private String name;
  12. private String type;
  13. private Date date;
  14. @Override
  15. protected Object clone() throws CloneNotSupportedException {
  16. Object obj = super.clone();
  17. Video v=(Video)obj;
  18. v.date= (Date) this.date.clone();
  19. return obj;
  20. }
  21. @Override
  22. public String toString() {
  23. return "Video{" +
  24. "name='" + name + '\'' +
  25. ", type='" + type + '\'' +
  26. ", date=" + date +
  27. '}';
  28. }
  29. public String getName() {
  30. return name;
  31. }
  32. public void setName(String name) {
  33. this.name = name;
  34. }
  35. public String getType() {
  36. return type;
  37. }
  38. public void setType(String type) {
  39. this.type = type;
  40. }
  41. public Date getDate() {
  42. return date;
  43. }
  44. public void setDate(Date date) {
  45. this.date = date;
  46. }
  47. public Video(String name, String type, Date date) {
  48. this.name = name;
  49. this.type = type;
  50. this.date = date;
  51. }
  52. public Video() {
  53. }
  54. }

这里涉及到浅拷贝和深拷贝的区别:
基本数据类型:赋值,赋值之后两个变量互不影响
引用数据类型:赋址,两个变量具有相同的引用,指向同一个对象,相互之间有影响
image.png

适配器

对象适配器优点:
一个对象适配器可以把多个不同的适配者适配到同一个目标
可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。

类适配器缺点:
对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。

适用场景:
系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。

image.png
转化器接口

  1. //接口转换器的抽象实现~
  2. public interface NetToUSB {
  3. //处理上网请求
  4. void HandelAdapter();
  5. }

网线 真正调用的方法

  1. //要被适配器的类:网线
  2. public class Adaptee {
  3. public void request(){
  4. System.out.println("连接网线上网");
  5. }
  6. }

适配器

  1. //真正的适配器 需要连接USB 连接网线
  2. //1)继承的方式
  3. //对象的方式
  4. public class Adapter implements NetToUSB {
  5. private Adaptee adaptee;
  6. public Adapter(Adaptee adaptee) {
  7. this.adaptee = adaptee;
  8. }
  9. @Override
  10. public void HandelAdapter() {
  11. adaptee.request();
  12. }
  13. }

电脑

  1. //客户端类:需要上网,插不上网线
  2. public class Computer {
  3. public void net(NetToUSB netToUSB){
  4. //上网的具体实现,找一个转接头
  5. netToUSB.HandelAdapter();
  6. }
  7. public static void main(String[] args) {
  8. Computer computer=new Computer();//电脑
  9. Adaptee adapte=new Adaptee();//网线
  10. Adapter adapter=new Adapter(adapte);//转接器
  11. computer.net(adapter);
  12. }
  13. }

小结:采用多态的方式,这适配器中有参构造加入网线,调用网线的处理上网方法。

桥接模式

image.png

image.png
image.png

品牌接口及实现

  1. public interface Brand {
  2. void info();
  3. }
  4. ---------------
  5. //苹果品牌
  6. public class Apple implements Brand{
  7. @Override
  8. public void info() {
  9. System.out.print("苹果");
  10. }
  11. }
  12. --------------------------
  13. //联想品牌
  14. public class Lenovo implements Brand{
  15. @Override
  16. public void info() {
  17. System.out.print("联想");
  18. }
  19. }

类型

  1. //电脑
  2. public abstract class Computer {
  3. //组合,品牌
  4. protected Brand brand;
  5. public Computer(Brand brand) {
  6. this.brand = brand;
  7. }
  8. public void info(){
  9. brand.info();
  10. }
  11. }
  12. class Notebook extends Computer{
  13. public Notebook(Brand brand) {
  14. super(brand);
  15. }
  16. @Override
  17. public void info() {
  18. super.info();
  19. System.out.println("笔记本");
  20. }
  21. }
  22. class Desktop extends Computer {
  23. @Override
  24. public void info() {
  25. super.info();
  26. System.out.println("电脑");
  27. }
  28. public Desktop(Brand brand) {
  29. super(brand);
  30. }
  31. }

小结:
桥接模式(Bridge Pattern):将抽象部分与它的实现部分分离,使它们都可以独立地变化。

桥接模式 通过将继承改为组合的方式来解决这个问题。 具体来说, 就是抽取其中一个维度并使之成为独立的类层次, 这样就可以在初始类中引用这个新层次的对象, 从而使得一个类不必拥有所有的状态和行为。

优点:

  • 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本

  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来!

缺点:

  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。

  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。

相关链接:https://blog.csdn.net/weixin_39296283/article/details/104953668

装饰者模式

  1. 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
  2. 这里提到的动态的将新功能附加到对象和ocp 原则,在后面的应用实例上会以代码的形式体现,

要点: 装饰者与被装饰者拥有共同的超类,继承的目的是继承类型,而不是行为

装饰者模式就像打包一个快递
主体:比如:陶瓷、衣服(Component) // 被装饰者
包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)

Component 主体:比如类似前面的Drink

ConcreteComponent 和Decorator
ConcreteComponent:具体的主体,
比如前面的各个单品咖啡

Decorator: 装饰者,比如各调料.
在如图的Component 与ConcreteComponent之间,如果ConcreteComponent 类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类
image.png

装饰者模式下的订单:2 份巧克力+一份牛奶的LongBlack

image.png


代码:

  1. //饮品抽象类 被装饰者
  2. public abstract class Drink {
  3. //描述
  4. public String dec;
  5. //价格
  6. private float price=0.0f;
  7. //计算费用的方法
  8. //子类实现
  9. public abstract float cost();
  10. public String getDec() {
  11. return dec;
  12. }
  13. public void setDec(String dec) {
  14. this.dec = dec;
  15. }
  16. public float getPrice() {
  17. return price;
  18. }
  19. public void setPrice(float price) {
  20. this.price = price;
  21. }
  22. }
  23. ----------------------
  24. public class LongBlack extends Coffee{
  25. public LongBlack() {
  26. setDec("美式咖啡");
  27. setPrice(5.0f);
  28. }
  29. }
  30. -----------------------
  31. //装饰者类
  32. public class Decorator extends Drink{
  33. private Drink obj;
  34. @Override
  35. public String getDec() {
  36. //obj.getDec 输出被装饰者的信息
  37. return this.dec+" "+this.getPrice()+" && "+obj.getDec();
  38. }
  39. public Decorator(Drink obj) {
  40. this.obj = obj;
  41. }
  42. @Override
  43. public float cost() {
  44. //getPrice自己的价格
  45. return this.getPrice()+obj.cost();
  46. }
  47. }
  48. //牛奶调味品
  49. public class Milk extends Decorator {
  50. public Milk(Drink obj) {
  51. super(obj);
  52. setDec("牛奶");
  53. setPrice(2.5f);
  54. }
  55. }
  56. //豆浆调味品
  57. public class Soy extends Decorator{
  58. public Soy(Drink obj) {
  59. super(obj);
  60. setDec("豆浆");
  61. setPrice(1.5f);
  62. }
  63. }
  64. ----------------------------------
  65. 测试:
  66. //1.点一份LongBlack
  67. Drink order=new LongBlack();
  68. System.out.println("费用1"+" "+order.cost());
  69. System.out.println("描述"+" "+order.getDec());
  70. //2.加一份牛奶
  71. order=new Milk(order);
  72. System.out.println("费用2"+" "+order.cost());
  73. System.out.println("描述2"+" "+order.getDec());

小结:
1.通过多态的方式创建LongBlack对象,在构造方法里设置价格和描述。
2.通过多态创建对象,传递LongBlack对象,在Milk构造方法里先调用super(obj),将引用传递。
3.此时调用的是Decorator的cost()和getDec(),将自己的价格加上 obj.cost()调用的是LongBlack的获取价格方法。 获取描述也是相同。
4.最主要的是通过组合的方式将Drink,每次传递引用给Decorator

https://blog.csdn.net/jason0539/article/details/22713711

组合模式

基本介绍
1) 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结
构,将对象组合成树状结构以表示“整体-部分”的层次关系。
2) 组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
3) 这种类型的设计模式属于结构型模式。
4) 组合模式使得用户对单个对象和组合对象的访问具有一致性,:组合能让客
户以一致的方式处理个别对象以及组合对象

原理类图
image.png

对原理结构图的说明-即(组合模式的角色及职责)
1) Component :这是组合中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理Component 子
部件, Component 可以是抽象类或者接口
2) Leaf : 在组合中表示叶子节点,叶子节点没有子节点

组合模式解决的问题
1) 组合模式解决这样的问题,当我们的要处理的对象可以生成一颗树形结构,而
我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑
它是节点还是叶子
2) 对应的示意图
image.png

代码:

  1. //Component
  2. public abstract class OrganizationComponent {
  3. private String name; //名字
  4. private String des;//描述
  5. //普通方法 不需要叶子节点实现
  6. protected void add(OrganizationComponent component){
  7. }
  8. protected void remove(OrganizationComponent component){
  9. }
  10. public OrganizationComponent(String name, String des) {
  11. this.name = name;
  12. this.des = des;
  13. }
  14. public String getName() {
  15. return name;
  16. }
  17. public void setName(String name) {
  18. this.name = name;
  19. }
  20. public String getDes() {
  21. return des;
  22. }
  23. public void setDes(String des) {
  24. this.des = des;
  25. }
  26. //打印方法 抽象需要子类实现
  27. protected abstract void print();
  28. }
  29. ----------------------------------------------------------
  30. //University就是管理者 可以管理(College)
  31. public class University extends OrganizationComponent{
  32. List<OrganizationComponent> components=new ArrayList<>();
  33. public University(String name, String des) {
  34. super(name, des);
  35. }
  36. @Override
  37. protected void add(OrganizationComponent component) {
  38. components.add(component);
  39. }
  40. @Override
  41. protected void remove(OrganizationComponent component) {
  42. components.remove(component);
  43. }
  44. @Override
  45. public String getName() {
  46. return super.getName();
  47. }
  48. @Override
  49. public String getDes() {
  50. return super.getDes();
  51. }
  52. @Override
  53. protected void print() {
  54. System.out.println("------------"+getName()+"--------------");
  55. components.forEach(x->x.print());
  56. }
  57. }
  58. ----------------------------------------------------
  59. //院校
  60. public class College extends OrganizationComponent{
  61. List<OrganizationComponent> components=new ArrayList<>();
  62. public College(String name, String des) {
  63. super(name, des);
  64. }
  65. @Override
  66. protected void add(OrganizationComponent component) {
  67. components.add(component);
  68. }
  69. @Override
  70. protected void remove(OrganizationComponent component) {
  71. components.remove(component);
  72. }
  73. @Override
  74. public String getName() {
  75. return super.getName();
  76. }
  77. @Override
  78. public String getDes() {
  79. return super.getDes();
  80. }
  81. @Override
  82. protected void print() {
  83. System.out.println("------------"+getName()+"--------------");
  84. components.forEach(x->x.print());
  85. }
  86. }
  87. ---------------------------------------------
  88. //系 也就是最后的子节点 不需要实现方法
  89. public class Department extends OrganizationComponent{
  90. @Override
  91. public String getName() {
  92. return super.getName();
  93. }
  94. @Override
  95. public String getDes() {
  96. return super.getDes();
  97. }
  98. public Department(String name, String des) {
  99. super(name, des);
  100. }
  101. @Override
  102. protected void print() {
  103. System.out.println(getName());
  104. }
  105. }
  106. ------------------------------------测试-------------------------------
  107. //从大到小创建对象 学校
  108. OrganizationComponent university = new University("清华大学", "名牌大学");
  109. //创建学院
  110. OrganizationComponent college1 = new College("计算机学院", "计算机学院");
  111. OrganizationComponent infoCollege = new College("信息工程学院", "信息工程学院");
  112. //创建各个学院下面的系(专业)
  113. college1.add(new Department("软件工程", "软件工程"));
  114. college1.add(new Department("网络工程", "网络工程"));
  115. college1.add(new Department("计算机科学与技术", "老牌专业"));
  116. //
  117. infoCollege.add(new Department("通信工程", "通信工程不好学"));
  118. infoCollege.add(new Department("信息工程", "信息工程好学"));
  119. //将学院加入到学校
  120. university.add(college1);
  121. university.add(infoCollege);
  122. university.print();
  123. ------------清华大学--------------
  124. ------------计算机学院--------------
  125. 软件工程
  126. 网络工程
  127. 计算机科学与技术
  128. ------------信息工程学院--------------
  129. 通信工程
  130. 信息工程
  131. Process finished with exit code 0

流程图
image.png
小结:

1、 组合模式用于组合多个对象所构成的树形结构层次。
2、组合模式包含抽象构建,叶子构建,和容器构建三种角色。
3、组合模式的优点是解决客户端不好统一对待两种类型的类,缺点是面对一些特殊要求时不好办!

组合模式的注意事项和细节
1) 简化客户端操作。客户端只需要面对一致的对象而不用考虑整体部分或者节点叶子
的问题。
2) 具有较强的扩展性。当我们要更改组合对象时,我们只需要调整内部的层次关系,
客户端不用做出任何改动.
3) 方便创建出复杂的层次结构。客户端不用理会组合里面的组成细节,容易添加节点
或者叶子从而创建出复杂的树形结构
4) 需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式.
5) 要求较高的抽象性,如果节点和叶子有很多差异性的话,比如很多方法和属性
都不一样,不适合使用组合模式

外观模式

外观模式(Facade Pattern)也称为过程模式,是结构性模式。外观模式为子系统的一组接口提供了一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。外观模式可以理解为转换一群接口,客户只要调用这一个接口而不用调用多个接口才能达到目的,也不需关心这个子系统的内部细节。就是解决多个复杂接口带来的使用困难,起到简化用户操作的作用。
[

](https://blog.csdn.net/qq_45034708/article/details/114972361)
类图:
image.png

  • Facade外观类:提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给相应子系统对象。
  • System子系统:处理Facade对象指派的任务,是功能的实际提供者。
  • Client客户端:外观接口调用测试者。

关系图

image.png

代码
客户端:

public class Client {

  public static void main(String[] args) {
    HomeTheaterFacade facade=new HomeTheaterFacade();
    //调用方法
    facade.ready();
    facade.play();
    facade.end();
  }

}

外观组合类

public class HomeTheaterFacade {

  //定义各个对象
  private DVDPlayer dvdPlayer;
  private Screen screen;
  private Stereo stereo;
  private Popcorn popcorn;
  private Projector projector;
  private Light light;

  public HomeTheaterFacade() {
    this.dvdPlayer = DVDPlayer.getInstance();
    this.screen = Screen.getInstance();
    this.stereo = Stereo.getInstance();
    this.popcorn = Popcorn.getInstance();
    this.projector = Projector.getInstance();
    this.light = Light.getInstance();
  }

  //看电影前准备工作
  public void ready(){
    popcorn.on();
    dvdPlayer.on();
    screen.down();
    stereo.on();
    light.down();
  }


  public void play(){
    dvdPlayer.player();
  }


  public void pause(){
    dvdPlayer.pause();
  }


  public void end(){
    popcorn.off();
    light.up();
    screen.up();
    projector.off();
    stereo.off();
    dvdPlayer.off();
  }
}

投影仪

public class Projector {
  //使用单列模式饿汉式
  private static final Projector projector=new Projector();

  private Projector() {
  }

  public static Projector getInstance(){
    return projector;
  }

  public void on(){
    System.out.println("投影仪开机了!");
  }

  public void off(){
    System.out.println("投影仪关机了!");
  }

  public void player(){
    System.out.println("投影仪开始工作!");
  }

  public void pause(){
    System.out.println("投影仪暂停了!");
  }
}

DVD

public class DVDPlayer {
  //使用单列模式饿汉式
  private static final DVDPlayer dvdPlayer=new DVDPlayer();

  private DVDPlayer() {
  }

  public static DVDPlayer getInstance(){
    return dvdPlayer;
  }

  public void on(){
    System.out.println("DVD开机了!");
  }

  public void off(){
    System.out.println("DVD关机了!");
  }

  public void player(){
    System.out.println("DVD开始播放!");
  }

  public void pause(){
    System.out.println("DVD暂停了!");
  }
}

总结

  1. 外观模式对客户端与子系统的耦合关系,让子系统内部的模块更易维护和扩展。
  2. 外观模式对外屏蔽了子系统的细节,因此外观模式降低了客户端对子系统使用的复杂性。
  3. 当系统需要进行分层设计时,可以考虑外观模式帮我们更好的划分访问的层次。
  4. 不能过多的或者不合理的使用外观模式,使用外观模式好,还是直接调用模块好。要以让系统有层次,利于维护为目的。
  5. 在维护一个遗留的大型系统时,可能这个系统已经变得非常难以维护和扩展,此时可以考虑为新系统开发一个Facade类,来提供遗留系统的比较清晰简单的接口,让新系统与Facade类交互,提高复用性。

[

](https://blog.csdn.net/qq_45034708/article/details/114972361)

享元模式

基本介绍
1) 享元模式(Flyweight Pattern) 也叫 蝇量模式: 运用共享技术有效地支持大量细粒度的对象
2) 常用于系统底层开发,解决系统的性能问题。像 数据库连接池,里面都是创建好的连接对象,在 这些连接对象中有我们需要的则直接拿来用,避
免重新创建,如果没有我们需要的,则创建一个
3) 享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以
降低系统内存,同时提高效率
4) 享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式

享元模式的原理类图
image.png
 Flyweight: 享元接口,通过这个接口传入外部状态并作用于外部状态;
 ConcreteFlyweight: 具体的享元实现对象,必须是可共享的,需要封装享元对象的内部状态;
 UnsharedConcreteFlyweight: 非共享的享元实现对象,并不是所有的享元对象都可以共享,非共享的享元对象通常是享元对象的组合对象;
 FlyweightFactory: 享元工厂,主要用来创建并管理共享的享元对象,并对外提供访问共享享元的接口;
[

](https://blog.csdn.net/justloveyou_/article/details/55045638)
代码:

//抽象享元角色类
public abstract class WebSite {
  //一个示意性方法,参数state是外部状态
  protected abstract void use(User user);
}


//具体享元角色类
public class ConcreteWebSite extends WebSite{

  //共享部分 内部类型
  private  String type="";

  /**
   * 构造函数,内蕴状态作为参数传入
   * @param type
   */
  public ConcreteWebSite(String type) {
    this.type = type;
  }

  /**
   * 外蕴状态作为参数传入方法中,改变方法的行为,
   * 但是并不改变对象的内蕴状态。
   */
  @Override
  protected void use(User user) {
    System.out.println("网站的发布形式为:"+type+"    "+"使用者为:"+user.getName());
  }
}

//享元工厂
public class WebSiteFactory {

  //网站类型缓存池
  private HashMap<String,ConcreteWebSite> pool=new HashMap<>();


  public WebSite getWebSiteCategory(String type){
    //判断池中是否有 无则新建
    if (!pool.containsKey(type)){
      pool.put(type,new ConcreteWebSite(type));
    }

    return (WebSite) pool.get(type);
  }

  public int getSize(){
   return pool.size();
  }
}

--------------------测试-------------------
      public static void main(String[] args) {
  WebSiteFactory webSiteFactory=new WebSiteFactory();
  WebSite webSite = webSiteFactory.getWebSiteCategory("微信公众号");
  webSite.use(new User("张三"));


    WebSite webSite2 = webSiteFactory.getWebSiteCategory("微博1");
    webSite2.use(new User("李四"));


    WebSite webSite3 = webSiteFactory.getWebSiteCategory("微博");
    webSite3.use(new User("王五"));

    WebSite webSite4 = webSiteFactory.getWebSiteCategory("微博");
    webSite4.use(new User("二狗"));

    System.out.println(webSiteFactory.getSize());

  }

网站的发布形式为:微信公众号    使用者为:张三
网站的发布形式为:微博1    使用者为:李四
网站的发布形式为:微博    使用者为:王五
网站的发布形式为:微博    使用者为:二狗
3

小结:
享元模式的核心在于享元工厂类,享元工厂类的作用在于提供一个用于存储享元对象的享元池,用户需要对象时,首先从享元池中获取,如果享元池中不存在,则创建一个新的享元对象返回给用户,并在享元池中保存该新增对象。

享元模式以共享的方式高效地支持大量的细粒度对象,享元对象能做到共享的关键是区分内部状态(Internal State)和外部状态(External State)。其中:

内部状态 是存储在享元对象内部并且不会随环境改变而改变的状态,因此内部状态可以共享。
外部状态 是随环境改变而改变的、不可以共享的状态。享元对象的外部状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的。
[

](https://blog.csdn.net/justloveyou_/article/details/55045638)

在Integer中有使用到

返回表示指定int值的Integer实例。 如果不需要新的Integer实例,则通常应优先使用此方法而不是构造函数Integer(int) ,因为此方法通过缓存频繁请求的值可能会产生明显更好的空间和时间性能。 此方法将始终缓存 -128 到 127(含)范围内的值,并且可能缓存此范围之外的其他值。
参数:
i – 一个int数值。
返回:
表示i的Integer实例。
  public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

例子

Integer x = Integer.valueOf(127);
Integer y = new Integer(127);
Integer z = Integer.valueOf(127);
Integer w = new Integer(127);
System.out.println(x.equals(y)); // true
System.out.println(x == y ); // false
System.out.println(x == z ); // true
System.out.println(w == x ); // false
System.out.println(w == y ); // false