• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
在赶路的我
博客园    首页    新随笔    联系   管理    订阅  订阅

设计模式

设计模式

设计模式的目的

  • 在编写软件的过程中,我们会面临来自耦合性,内聚性以及可维护性,可扩展性,重要性,灵活性等多方面的挑战,设计模式就是为了让程序具有更好的
    • 代码重用性
    • 可读性
    • 可扩展性
    • 可靠性
    • 使程序呈现高内聚低耦合的特性

设计模式的七大原则

  • 设计模式原则,其实就是程序员在变成时候,应当遵守的原则,也是各种设计模式的基础(即设计模式为什么这样设计的依据)

设计模式常用的七大原则有

  1. 单一职责原则
  2. 接口隔离原则
  3. 依赖倒转原则(倒置)
  4. 里氏替换原则
  5. 开闭原则
  6. 迪米特法则
  7. 合成复用原则

单一职责原则

  • 对于类来说,即一个类应该只负责一项职责,如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A时,可能造成职责2的执行错误,所以需要将A的粒度分解为A1,A2
  • 单一职责原则注意事项和细节
    • 降低类的复杂度,一个类只负责一项职责
    • 提高类的可读性,可维护性
    • 降低变更引起的风险
    • 通常情况下,我们应当遵守单一职责原则,只有逻辑足够简单,才可以在代码级别违反单一职责原则,只有类中方法足够少,可以在方法级别保持单一职责原则

接口隔离原则

  • 客户端不应该依赖他不需要的接口,即一个类对另一个类的依赖应该建立在最小的接口上

  • 例子:

    • 类A通过接口Interface1依赖类B,类C通过接口Interface1依赖类D,如果接口Interface1对于类A和C来说不是最小的接口,那么类B和D必须去实现他们不需要的方法。

      • 类图

      • 代码

        • package demo1;
          
          public class Test {
          	
          	public static void main(String[] args) {
          	
          		//A通过接口1来依赖B中的方法1,2,3
          		A a = new A();
          		a.depend1(new B());
          		a.depend2(new B());
          		a.depend3(new B());
          		//A通过接口1来依赖B中的方法1,4,5
          		C c = new C();
          		c.depend1(new D());
          		c.depend4(new D());
          		c.depend5(new D());
          		
          	}
          	
          }
          
          interface Interface1{
          	void method1();
          	void method2();
          	void method3();
          	void method4();
          	void method5();
          }
          
          class B implements Interface1{
          
          	@Override
          	public void method1() {
          		// TODO Auto-generated method stub
          		System.out.println("B实现了Interface1方法1");
          	}
          
          	@Override
          	public void method2() {
          		// TODO Auto-generated method stub
          		System.out.println("B实现了Interface1方法2");
          	}
          
          	@Override
          	public void method3() {
          		// TODO Auto-generated method stub
          		System.out.println("B实现了Interface1方法3");
          	}
          
          	@Override
          	public void method4() {
          		// TODO Auto-generated method stub
          		System.out.println("B实现了Interface1方法4");
          	}
          
          	@Override
          	public void method5() {
          		// TODO Auto-generated method stub
          		System.out.println("B实现了Interface1方法5");
          	}
          	
          }
          
          
          class D implements Interface1{
          
          	@Override
          	public void method1() {
          		// TODO Auto-generated method stub
          		System.out.println("D实现了Interface1方法1");
          	}
          
          	@Override
          	public void method2() {
          		// TODO Auto-generated method stub
          		System.out.println("D实现了Interface1方法2");
          	}
          
          	@Override
          	public void method3() {
          		// TODO Auto-generated method stub
          		System.out.println("D实现了Interface1方法3");
          	}
          
          	@Override
          	public void method4() {
          		// TODO Auto-generated method stub
          		System.out.println("D实现了Interface1方法4");
          	}
          
          	@Override
          	public void method5() {
          		// TODO Auto-generated method stub
          		System.out.println("D实现了Interface1方法5");
          	}
          	
          }
          
          
          class A{
          
          	//通过Interface1接口来依赖B方法1,2,3
          	public void depend1(Interface1 interface1) {
          		interface1.method1();
          	}
          	public void depend2(Interface1 interface1) {
          		interface1.method2();
          	}
          	public void depend3(Interface1 interface1) {
          		interface1.method3();
          	}
          }
          
          class C{
          	
          	//通过Interface2接口来依赖D中的方法1,4,5
          	public void depend1(Interface1 interface1) {
          		interface1.method1();
          	}
          	public void depend4(Interface1 interface1) {
          		interface1.method4();
          	}
          	public void depend5(Interface1 interface1) {
          		interface1.method5();
          	}
          }
          
          
    • 解决方法:

      • 将接口Interface1拆分为独立的几个接口

      • 类图

      • 代码

        • package demo1;
          
          public class Test {
          	
          	public static void main(String[] args) {
          	
          		A a = new A();
          		a.depend1(new B());
          		a.depend2(new B());
          		a.depend3(new B());
          		
          		C c = new C();
          		c.depend1(new D());
          		c.depend4(new D());
          		c.depend5(new D());
          		
          	}
          	
          }
          //将接口分解
          interface Interface1{
          	void method1();
          }
          interface Interface2{
          	void method2();
          	void method3();
          }
          interface Interface3{
          	void method4();
          	void method5();
          }
          
          class B implements Interface1,Interface2{
          
          	@Override
          	public void method1() {
          		// TODO Auto-generated method stub
          		System.out.println("B实现了Interface1方法1");
          	}
          
          	@Override
          	public void method2() {
          		// TODO Auto-generated method stub
          		System.out.println("B实现了Interface1方法2");
          	}
          
          	@Override
          	public void method3() {
          		// TODO Auto-generated method stub
          		System.out.println("B实现了Interface1方法3");
          	}
          	
          }
          
          
          class D implements Interface1,Interface3{
          
          	@Override
          	public void method1() {
          		// TODO Auto-generated method stub
          		System.out.println("D实现了Interface1方法1");
          	}
          
          	@Override
          	public void method4() {
          		// TODO Auto-generated method stub
          		System.out.println("D实现了Interface1方法4");
          	}
          
          	@Override
          	public void method5() {
          		// TODO Auto-generated method stub
          		System.out.println("D实现了Interface1方法5");
          	}
          }
          
          
          class A{
          
          	//通过Interface1接口来依赖B方法1,2,3
          	public void depend1(Interface1 interface1) {
          		interface1.method1();
          	}
          	public void depend2(Interface2 interface2) {
          		interface2.method2();
          	}
          	public void depend3(Interface2 interface2) {
          		interface2.method3();
          	}
          }
          
          class C{
          	
          	//通过Interface2接口来依赖D中的方法1,4,5
          	public void depend1(Interface1 interface1) {
          		interface1.method1();
          	}
          	public void depend4(Interface3 interface3) {
          		interface3.method4();
          	}
          	public void depend5(Interface3 interface3) {
          		interface3.method5();
          	}
          }
          

依赖倒转原则

  • 介绍

    • 高层模块不应该依赖低层模块,二者都应该依赖其抽象
    • 抽象不应该依赖细节,细节应该依赖抽象
    • 依赖倒装(倒置)的中心思想是面向接口编程
    • 依赖倒转原则是基于这样的设计理念:相对于细节的多边性,抽象的东西要稳定的多,以抽象为基础搭建的架构要比以细节为基础的架构要稳定的多,在java中,抽象指定是接口或者抽象类,细节就是具体的实现类。
    • 使用接口或者抽象类的目的就是制定好规范,而不涉及任何具体的操作,把展示细节的任务交个他们的实现类去完成。
  • 依赖关系传递的三种方式

    1. 接口传递依赖

      • package demo1;
        
        public class Test {
        	public static void main(String[] args) {
        		WatchType1 watchType1 = new WatchType1();
        		watchType1.border(new ChangHong());	
        	}	
        }
        //实现什么牌子的计算机正在播放什么样的节目
        interface TvType{
        	String getTvType();
        }
        
        class ChangHong implements TvType{
        	@Override
        	public String getTvType() {
        		// TODO Auto-generated method stub
        		return "长虹";
        	}
        }
        
        interface WatchType{
        	//接口依赖传递
        	void border(TvType tvType);
        }
        
        class WatchType1 implements WatchType{
        	public void border(TvType tvType) {
        		System.out.println(tvType.getTvType()+"牌子播放了WatchType1节目");
        	}
        }
        
    2. 构造方法传递

      • class WatchType2 implements WatchType{
        	//构造参数传递
        	private TvType tvType;
        	public WatchType2(TvType tvType) {
        		this.tvType = tvType;
        	}
        	
        	public void border(TvType tvType) {
        		System.out.println(this.tvType.getTvType()+"牌子播放了WatchType1节目");
        	}
        	
        }
        
    3. setter方式传递

      • class WatchType3 implements WatchType{
        	
        	private TvType tvType;
        	
        	public void setTvType(TvType tvType) {
        		this.tvType=tvType;
        	}
        	
        
        	@Override
        	public void border(TvType tvType) {
        		// TODO Auto-generated method stub
        		System.out.println(this.tvType.getTvType()+"牌子播放了WatchType1节目");
        	}
        	
        }
        
        
  • 依赖倒转原则的注意事项和细节

    1. 低层模块尽量都要有抽象类或者接口,或者两者都有,程序稳定性更好
    2. 变量的声明类型尽量是抽象类或者接口,这样我们的变量引用和实际对象间,就存在一个缓冲层,利于程序扩展和优化
    3. 继承时遵循里氏替换原则

里氏替换原则

  • 背景:

    1. 继承包含这样一层含义:父类中凡是已经实现好的方法,实际上实在设定规范和契约,虽然他不强制要求子类必须遵守这些契约,但是如果子类对这些已经实现好的方法任意修改,就会对整个继承体系造成破坏
    2. 继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障
    3. 于是在编程的时候如何使用继承=>里氏替换原则
  • 介绍:

    1. 里氏替换原则是由麻省理工的一位姓里的女士在1988年提出来的
    2. 在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类中的方法
    3. 继承实际上是让两个类耦合性增强了,在适当的情况下,可以通过聚合,组合,依赖来解决问题。
  • 问题例子:

    • package demo1;
      
      public class Test {
      	public static void main(String[] args) {
      		B b = new B();
      		System.out.println(b.addAdd(1,20, 10));
      	}
      }
      //要求B继承A来使用A中的方法add,但是存在一定的风险
      class A{
      	public int add(int a,int b) {
      		return a+b;
      	}
      }
      class B extends A{
      	//在这里不小心重写了add方法,且造成了破坏,导致了addAdd方法原来想用的是父类的方法add变成了重写后的add方法
      	//所以继承带来方便的同时也带来了程序的侵入性
      //	public int add(int a,int b) {
      //		return a-b;
      //	}
      	public int addAdd(int c,int a,int b) {
      		return c+add(a, b);
      	}
      }
      
  • 解决方法

    • 通用的做法是:原来的父类和子类都继承一个更加通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替

    • package demo1;
      
      public class Test {
      	public static void main(String[] args) {
      		B b = new B();
      		System.out.println(b.addAdd(1,20, 10));
      	}
      }
      //要求B继承A来使用A中的方法add,但是存在一定的风险
      class A extends Base{
      	public int add(int a,int b) {
      		return a+b;
      	}
      }
      class B extends Base{
      	//在这里不小心重写了add方法,且造成了破坏,导致了addAdd方法原来想用的是父类的方法add变成了重写后的add方法
      	//所以继承带来方便的同时也带来了程序的侵入性
      //	public int add(int a,int b) {
      //		return a-b;
      //	}
      	//组合A和B
      	private A a = new A();
      	public int addAdd(int c,int a,int b) {
      		return c+this.a.add(a, b);
      	}
      }
      //解决方法,让A和B继承一个更加基础的类
      class Base{}
      

开闭原则

  • 介绍
    1. 开闭原则是编程中最基础,最重要的设计原则
    2. 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关不(对使用方),用抽象构建框架,用实现扩展细节
    3. 当软件需要变化时候,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化
    4. 编程中遵循其他原则,以及使用设计模式的目的及时遵循开闭原则

迪米特法则

  • 介绍:
    1. 一个对象应该对其他对象保持最少的了解
    2. 类与类关系月密切,耦合度越大
    3. 迪米特法则又叫最少知道原则,寄一个类对自己依赖的类知道的越少越好,也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部,对外除了提供public方法,不对外泄漏任何信息
    4. 迪米特法则还有一个更简单的定义:只与直接的朋友通信
      • 直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系,耦合的方式很多,依赖,关联,组合,聚合等,其中,我们称出现成员变量,方法参数,方法返回值的类为直接朋友,而出现在局部变量中的类不是直接朋友,也就是说,陌生类最好不要以局部变量的形式出现在类的内部。
  • 注意事项:
    • 迪米特法则的核心是降低类之间的耦合,并不是要求完全没有依赖关系

组合和聚合

  • 当我们需要使用其他类的方法时候,不推荐直接用继承,而是使用组合和聚合的方法

  • 以上的类图中有四种方法实现引用其他类的方法

    1. 继承(不推荐使用)
    2. 使用方法参数的形式来传入类变量
    3. 使用setter方法来注入其他类(聚合)
    4. 使用new关键字直接在类中创建其他类的对象(组合)

设计模式的核心思想

  1. 找出应用中可能需要变化之外,把他们独立出来,不要和那些不需要变化的代码混在一起
  2. 针对接口编程,而不是针对实现编程
  3. 为了交互对象之间的松耦合设计而努力

UML类图简介

  • UML--Unified modeling language UML(统一建模语言),是一种用于软件系统分析和设计的语言工具
  • UML本身是一套符号的规定,就像数学符号和化学符号一样,这些符号用于描述软件模型中的各个元素和他们之间的关系,比如类,接口,实现,泛化,依赖,组合,聚合等

类图

  1. 用于描述系统中的类本身的组成和类之间的各种静态关系
  2. 类之间的关系有:依赖,泛化(继承),实现,关联,聚合,组合
  • 依赖(虚线箭头)

    • 只要是在类中用到了对方,他们之间就存在依赖关系,以下是存在依赖关系的几种常见的情况
      1. 为类的成员变量
      2. 为方法的返回类型
      3. 为方法的参数类型
      4. 方法中使用到了
  • 泛化(空心实线箭头)

    • 泛化关系实际上就是指继承关系,他是依赖关系的特例
  • 实现(空心虚线箭头)

    • 他也是依赖关系的特例
  • 关联(Association)

    • 关联关系实际上就是类与类之间的联系,他也是依赖关系的特例
    • 关联具有导航性:即双向关系或者单向关系
    • 关联具有多重性:如‘1’表示有且仅有一个,‘0...’表示0个或者多个,‘0,1’表示0个或者1个,‘n..m’表示n到m个,‘m...’表示至少m个
  • 聚合(Aggregation)

    • 聚合关系表示的是整体和部分的关系,整体和部分是可以被分开的,聚合关系是关联关系的特例,所以聚合具有导航性和多重性

    • 如电脑和鼠标和显示器的关系就是聚合关系

  • 组合

    • 也是整体与部分的关系,但是整体和部分不可以分开

    • 如人和头就是组合关系,身份证和人就是聚合关系

设计模式的分类

  • 设计模式分为三种类型,共23种

创建型模式

  • 单例模式
  • 抽象工厂模式
  • 原型模式
  • 建造者模式
  • 工厂模式

结构型模式

  • 适配器模式
  • 桥接模式
  • 装饰模式
  • 组合模式
  • 外观模式
  • 享元模式
  • 代理模式

行为型模式

  • 模板方法模式
  • 命令模式
  • 访问者模式
  • 迭代器模式
  • 观察者模式
  • 中介者模式
  • 备忘录模式
  • 解释器模式(Interpreter模式)
  • 状态模式
  • 策略模式
  • 职责链模式(责任链模式)

23中设计模式

单例模式

  • 所谓单例设计模式,就是采取一定的方法保证整个的软件系统中,对某个类只能存在一个对象实例,并且只提供一个静态方法取得器对象的实例

静态常量饿汉式

class Singleton {
    //私有化构造器
    private Singleton(){};
    //使用静态常量
    private static final Singleton singleton = new Singleton();
    //使用静态方法返回
    public static Singleton getInstance(){
        return singleton;
    }
}

优点

  • 写法简单,在类装载的时候就完成了实例化,避免了多线程问题。

缺点

  • 在类装载的时候就完成了实例化,没有达到懒加载的效果,如果其他方式导致的类装载,从始至终都没有使用过这个实例,可能就会造成内存浪费。

静态代码块饿汉式

class Singleton {
    //私有化构造器
    private Singleton(){};
    //使用静态常量
    private static final Singleton singleton;
    //在静态代码块中实例化
    static{
        singleton = new Singleton();
    }
    //使用静态方法返回
    public static Singleton getInstance(){
        return singleton;
    }
}
  • 缺点和优点和上面一样的

线程不安全懒汉式

class Singleton {
    //私有化构造器
    private Singleton(){};
    //静态变量
    private static final Singleton singleton;
    //使用静态方法来创建实例
    public static Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

优点和缺点

  • 起到了懒加载的效果,但是存在多线程问题
  • 在实际开发中不能使用这种方式创建单例

线程安全懒汉式

class Singleton {
    //私有化构造器
    private Singleton(){};
    //静态变量
    private static final Singleton singleton;
    //使用静态方法来创建实例
    public static synchronized Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

优点

  • 解决了线程安全问题

缺点

  • 加锁的目的是只让一个线程实例化,但是实例化过后,每一条线程在获取单例的时候都需要同步,效率下降
  • 在实际开发中不推荐使用

同步代码块懒汉式

class Singleton {
    //私有化构造器
    private Singleton(){};
    //静态变量
    private static final Singleton singleton;
    //使用静态方法来创建实例
    public static Singleton getInstance(){
        if(singleton==null){
            synchronized(Singleton.class){
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

没有优点只有缺点

  • 本想对上一种方法的优化,但是最后既没有起到线程安全,也没有优化

(双重检查)DoubleCheck

class Singleton {
    //私有化构造器
    private Singleton(){};
    //静态变量
    private static final Singleton singleton;
    //使用静态方法来创建实例
    public static Singleton getInstance(){
        if(singleton==null){
            synchronized(Singleton.class){
                if(singleton==null)
                singleton = new Singleton();
            }
        }
        return singleton;
    }
}

优点

  • 是懒汉式单例中最好的解决方法
  • 达到了懒加载,效率较高
  • 在实际开发中推荐使用这种方式的单例模式

静态内部类

class Singleton {
    //私有化构造器
    private Singleton(){};
    //使用静态内部类,静态内部类在Singleton被装载的时候不会被装载
    private static class SingletonInner {
        private static final Singleton instance = new Singleton();
    }
    //使用静态方法返回
    public static Singleton getInstance(){
        return SingletonInner.instance;
    }
}

优点

  • 既实现了懒加载,也保证了线程安全,效率高
  • 利用类的装载机制来保证初始化实例的时候只有一个线程,保证了线程安全
  • 类的静态属性只会在第一次加载类的时候初始化

枚举方式(JDK1.5)

enum Singleton {
    INSTANCE;
}

优点

  • 线程安全,效率高,防止反序列化创建对象

单例模式总结

  • 单例模式一共有四种方法创建

    1. 饿汉式
    2. 懒汉式(双重检查)
    3. 静态内部类
    4. 枚举

JDK中RunnTime类就使用到了单例模式中的饿汉式方法

那些场景需要使用到单例模式

  • 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
  • 创建对象时候耗时过多或者耗费资源过多(如RunnTime)
  • 经常用到的对象,工具类对象,频繁访问数据库或文件的对象(SqlSessionFactory)

工厂模式

简单工厂模式

  • 基本介绍

    1. 简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式
    2. 简单工厂模式:定义一个创建对象的类,由这个类来封装实例化对象的行为
    3. 在软件开发中,当我们会用到大量的创建莫种,莫类或者莫批对象时候,就会使用到工厂模式
  • 手机工厂生产根据手机型号生产不同的手机

    package com.chen.Deom1;
    
    import java.util.Scanner;
    
    public class PhoneSimpleFactoryDemo {
        public static void main(String[] args) {
    
            boolean flag = true;
    
            Scanner scanner = new Scanner(System.in);
            PhoneSimpleFactory factory = new PhoneSimpleFactory();
    
            while(flag){
                System.out.println("请输入需要购买的手机->");
                switch (scanner.next()){
                    case "AndroidPhone":
                        Phone androidPhone = factory.createPhone("AndroidPhone");
                        break;
                    case "ApplePhone":
                        Phone applePhone = factory.createPhone("ApplePhone");
                        break;
                    case "EXIT":
                        flag = false;
                        break;
                }
            }
    
        }
    }
    
    
    abstract class Phone {
        private String phoneType;
        private String phoneName;
        public Phone(String phoneType,String phoneName){
            this.phoneType=phoneType;
            this.phoneName=phoneName;
        }
    
        public abstract void setPhoneParam();
    
        public String getPhoneType() {
            return phoneType;
        }
    
        public String getPhoneName() {
            return phoneName;
        }
    }
    
    class PhoneSimpleFactory{
        //定义创建Phone实例的工厂方法
        public Phone createPhone(String phoneType){
    
            if (phoneType.equals("ApplePhone")){
                return new ApplePhone("ApplePhone","苹果手机");
            }else if(phoneType.equals("AndroidPhone")){
                return new AndroidPhone("AndroidPhone","安卓手机");
            }
            return null;
        }
    }
    
    class AndroidPhone extends Phone{
    
        public AndroidPhone(String phoneType, String phoneName) {
            super(phoneType, phoneName);
            setPhoneParam();
        }
    
        @Override
        public void setPhoneParam() {
            System.out.println("安卓手机参数");
        }
    }
    
    class ApplePhone extends Phone{
    
        public ApplePhone(String phoneType, String phoneName) {
            super(phoneType, phoneName);
            setPhoneParam();
        }
    
        @Override
        public void setPhoneParam() {
            System.out.println("苹果手机参数");
        }
    }
    
    

工厂方法模式

  • 定义了一个创建对象的抽象方法,由子类决定要实例化的类,工厂方法模式将对象的实例化推迟到子类。

  • pizza有多种品牌,总工厂提供生产pizza的抽象方法让各个不同的地方的pizza连锁工厂负责不同品牌的pizza的生产

    package com.chen.Deom1;
    
    import java.util.Scanner;
    
    public class MethodFactoryDemo {
        public static void main(String[] args) {
    
            Scanner scanner = new Scanner(System.in);
            boolean flag = true;
    
            while(flag){
                System.out.println("请输入pizza的生产地址->");
                switch (scanner.next()){
                    case "jiangsu":
                        JiangSuPizzaFactory jiangSuPizzaFactory = new JiangSuPizzaFactory();
                        Pizza pizza = jiangSuPizzaFactory.createPizza();
                        break;
                    case "guizhou":
                        GuiZhouPizzaFactory guiZhouPizzaFactory = new GuiZhouPizzaFactory();
                        Pizza pizza1 = guiZhouPizzaFactory.createPizza();
                        break;
                    case "exit":
                        flag = false;
                        break;
                    default:
                        flag =false;
                        break;
                }
            }
        }
    }
    
    //定义一个披萨
    class Pizza {
        private String type;
        private String price;
    
        public Pizza(String type,String price) {
            this.type=type;
            this.price=price;
        }
    
        public String getType() {
            return type;
        }
    
        public String getPrice() {
            return price;
        }
    
        @Override
        public String toString() {
            return "Pizza{" +
                    "type='" + type + '\'' +
                    ", price='" + price + '\'' +
                    '}';
        }
    }
    
    //定义不同牌子的披萨
    class JianSuPizza extends Pizza{
    
        public JianSuPizza(String type, String price) {
            super(type, price);
        }
    
    }
    class GuiZhouPizza extends Pizza{
    
        public GuiZhouPizza(String type, String price) {
            super(type, price);
        }
    }
    //定义一个总工厂,旗下的子类工厂负责生产不同牌子的披萨
    abstract class PizzaFactory {
        //定义一个抽象的工厂生产pizza的方法
        public abstract Pizza createPizza();
    }
    //各个厂负责生产不同的披萨
    class GuiZhouPizzaFactory extends PizzaFactory {
    
        @Override
        public Pizza createPizza() {
            Pizza pizza = null;
            try {
                System.out.println("贵州pizza工厂->");
                pizza = new GuiZhouPizza("香辣披萨",100+"");
                System.out.println(pizza);
            }catch (Exception e){
                e.printStackTrace();
            }
            return pizza;
        }
    }
    class JiangSuPizzaFactory extends PizzaFactory {
    
        @Override
        public Pizza createPizza() {
            Pizza pizza = null;
            try {
                System.out.println("江苏pizza工厂->");
                pizza = new GuiZhouPizza("香甜披萨",100+"");
                System.out.println(pizza);
            }catch (Exception e){
                e.printStackTrace();
            }
            return pizza;
        }
    }
    

抽象工厂模式

  • 定义了一个接口用于创建相关或有依赖关系的对象簇,而无需指明具体的类

    • 定义一个总的工厂接口,让子类去实现这个总工厂
  • 抽象工厂模式就可以将简单工厂模式和工厂方法模式进行整合。

  • 从设计层面看,抽象工厂模式就是对简单工厂模式的进一步抽象

    package com.chen.Deom1;
    
    import java.util.Scanner;
    
    public class MethodFactoryDemo {
        public static void main(String[] args) {
    
            Scanner scanner = new Scanner(System.in);
            boolean flag = true;
            PizzaFactory pizzaFactory;
    
            while(flag){
                System.out.println("请输入pizza的生产地址->");
                switch (scanner.next()){
                    case "jiangsu":
                        pizzaFactory  = new JiangSuPizzaFactory();
                        Pizza pizza = pizzaFactory.createPizza();
                        break;
    
                    case "guizhou":
                        pizzaFactory  = new GuiZhouPizzaFactory();
                        Pizza pizza1 = pizzaFactory.createPizza();
                        break;
                    case "exit":
                        flag = false;
                        break;
                    default:
                        flag =false;
                        break;
                }
            }
    
        }
    }
    
    //定义一个披萨
    class Pizza {
        private String type;
        private String price;
    
        public Pizza(String type,String price) {
            this.type=type;
            this.price=price;
        }
    
        public String getType() {
            return type;
        }
    
        public String getPrice() {
            return price;
        }
    
        @Override
        public String toString() {
            return "Pizza{" +
                    "type='" + type + '\'' +
                    ", price='" + price + '\'' +
                    '}';
        }
    }
    
    //定义不同牌子的披萨
    class JianSuPizza extends Pizza{
    
        public JianSuPizza(String type, String price) {
            super(type, price);
        }
    
    }
    class GuiZhouPizza extends Pizza{
    
        public GuiZhouPizza(String type, String price) {
            super(type, price);
        }
    }
    //定义一个总工厂,旗下的子类工厂负责生产不同牌子的披萨
    interface PizzaFactory {
        //定义一个抽象的工厂生产pizza的方法
        Pizza createPizza();
    }
    //各个厂负责生产不同的披萨
    class GuiZhouPizzaFactory implements PizzaFactory {
    
        @Override
        public Pizza createPizza() {
            Pizza pizza = null;
            try {
                System.out.println("贵州pizza工厂->");
                pizza = new GuiZhouPizza("香辣披萨",100+"");
                System.out.println(pizza);
            }catch (Exception e){
                e.printStackTrace();
            }
            return pizza;
        }
    }
    class JiangSuPizzaFactory implements PizzaFactory {
    
        @Override
        public Pizza createPizza() {
            Pizza pizza = null;
            try {
                System.out.println("江苏pizza工厂->");
                pizza = new GuiZhouPizza("香甜披萨",100+"");
                System.out.println(pizza);
            }catch (Exception e){
                e.printStackTrace();
            }
            return pizza;
        }
    }
    

工厂模式的总结

  • 简单工厂模式适用于生产对象的种类和复杂度相对较低
  • 抽象工厂模式适用于生产对象的种类和复杂度相对较高
  • 工厂模式的意义
    • 将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性
    • 依据设计模式的依赖抽象原则
      • 创建对象的实例时候,不要直接new,而是应该把这个new类的动作放在一个工厂的方法中,并返回,变量不要直接持有具体类的引用
      • 不要让类继承具体类,而是继承抽象类或者是实现interface
      • 不要覆盖基类中已经实现的方法

JDK源码中Calendar类使用到了简单工厂模式

if (aLocale.hasExtensions()) {
    String caltype = aLocale.getUnicodeLocaleType("ca");
    if (caltype != null) {
        switch (caltype) {
            case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                break;
            case "japanese":
                cal = new JapaneseImperialCalendar(zone, aLocale);
                break;
            case "gregory":
                cal = new GregorianCalendar(zone, aLocale);
                break;
        }
    }
}

原型模式

  • 使用传统方法创建多个克隆对象
Sheep sheep = new Sheep("clt",21,"white");
//创建多个和sheep相同的类
Sheep sheep1 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
Sheep sheep2 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
Sheep sheep3 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
Sheep sheep4 = new Sheep(sheep.getName(),sheep.getAge(),sheep.getColor());
  • 传统方式的优缺点
    • 理解简单
    • 在创建新的对象时候,总是需要重新获取原始对象的属性,如果创建的对象比较复杂,效率较低
    • 总是需要重新初始化对象,而不是动态的获得对象运行时候的状态,不够灵活
    • 解决方式
      • java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable,该接口表示该类能够复制且有复制的能力,也就是原型模式

原型模式介绍

  • 原型模式是指用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象
  • 原型模式是一种创建型设计模式,允许一个对象在创建另外一个可定制的对象,无需知道如何创建的细节

使用原型模式创建多个对象

package com.chen.Deom1;

import java.util.Calendar;

public class Test333 {

    public static void main(String[] args) throws CloneNotSupportedException {

        Sheep sheep = new Sheep("clt",21,"white");
        //创建多个和sheep相同的类
        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();
        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);

    }
}

class Sheep implements Cloneable{
    private String name;
    private int age;
    private String color;

    public Sheep(String name,int age,String color) {
        this.name=name;
        this.age=age;
        this.color=color;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
  • 在spring框架中也是用到了原型模式

浅拷贝

  • 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象
  • 对于数据类型是引用类型的成员变量,比如:数组,类对象等,那么浅拷贝只会进行引用传递,也就是只是将该成员变量的引用值复制一份给新的对象,因为实际上两个对象的该成员变量都指向同一个实例,在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

深拷贝

  • 复制对象的所有基本数据类型和成员变量值
  • 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变所引用的对象,直到该对象可达的所有对象,也就是说深拷贝要对整个对象进行拷贝
  • 深拷贝的实现方式
    • 重写clone方法来实现
    • 通过对象序列化实现(推荐使用)

第一种

package com.chen.Deom1;

import java.io.Serializable;
import java.util.Calendar;

public class Test333 {

    public static void main(String[] args) throws CloneNotSupportedException {

        Sheep friend = new Sheep("clt2",21,"black",null);
        Sheep sheep = new Sheep("clt",21,"white",friend);
        //创建多个和sheep相同的类
        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();
        sheep1.getFriend().setColor("red");
        System.out.println(sheep1.getFriend().hashCode());
        System.out.println(sheep2.getFriend().hashCode());
        System.out.println(sheep3.getFriend().hashCode());

    }
}

class Sheep implements Cloneable, Serializable {
    private String name;
    private int age;
    private String color;
    private Sheep friend;

    public Sheep(String name,int age,String color,Sheep friend) {
        this.name=name;
        this.age=age;
        this.color=color;
        this.friend=friend;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }


    public Sheep getFriend() {
        return friend;
    }

    public void setFriend(Sheep friend) {
        this.friend = friend;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                ", friend=" + friend +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Sheep sheep = null;
        sheep = (Sheep) super.clone();
        //针对于引用类型进行深拷贝
        if (friend!=null)
        sheep.setFriend((Sheep) friend.clone());
        return sheep;
    }
}

第二种

package com.chen.Deom1;

import java.io.*;
import java.util.Calendar;

public class Test333 {

    public static void main(String[] args) throws CloneNotSupportedException {

        Sheep friend = new Sheep("clt2",21,"black",null);
        Sheep sheep = new Sheep("clt",21,"white",friend);
        //创建多个和sheep相同的类
        Sheep sheep1 = (Sheep) sheep.getClone();
        Sheep sheep2 = (Sheep) sheep.getClone();
        Sheep sheep3 = (Sheep) sheep.getClone();
        sheep1.getFriend().setColor("red");
        System.out.println(sheep1.getFriend());
        System.out.println(sheep2.getFriend());
        System.out.println(sheep3.getFriend());

    }
}

class Sheep implements Cloneable, Serializable {
    private String name;
    private int age;
    private String color;
    private Sheep friend;

    public Sheep(String name,int age,String color,Sheep friend) {
        this.name=name;
        this.age=age;
        this.color=color;
        this.friend=friend;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }


    public Sheep getFriend() {
        return friend;
    }

    public void setFriend(Sheep friend) {
        this.friend = friend;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                ", friend=" + friend +
                '}';
    }

    //实现方式二
    public Object getClone(){

        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        Sheep sheep = null;


        try {

            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            sheep = (Sheep) ois.readObject();


        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                ois.close();
                bis.close();
                oos.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        return sheep;

    }
}

建造者模式

  • 建造者模式又叫生成器模式,是一种对象构建模式。他可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现的对象
  • 建造者模式是一步一步创建一个复杂对象,它允许用户只通过指定复杂对象的类型和内容就可以构建他们,用户不需要知道内部的具体构建细节。

使用传统方式建造房子

abstract class HouseBuilder {
    public abstract void builder1();
    public abstract void builder2();
    public abstract void builder3();

    public void startBuilder(){
        builder1();
        builder2();
        builder3();
    }
}

class SmallHouse extends HouseBuilder{


    @Override
    public void builder1() {
        System.out.println("小房子建造第一步");
    }

    @Override
    public void builder2() {
        System.out.println("小房子建造第二部");
    }

    @Override
    public void builder3() {
        System.out.println("小房子建造第二步");
    }

}

class BigHouse extends HouseBuilder{

    @Override
    public void builder1() {
        System.out.println("大房子建造第一步");
    }

    @Override
    public void builder2() {
        System.out.println("大房子建造第二步");
    }

    @Override
    public void builder3() {
        System.out.println("大房子建造第三步");
    }
}

建造者模式下的四个角色

  • Product(产品角色):一个具体的产品对象
  • Builder(抽象建造者):创建一个Product对象的各个部件指定的接口/抽象类
  • ConcreateBuilder(具体的建造者):实现接口,构建和装配各个部件
  • Director(指挥者):构建一个使用Builder接口的对象,它的主要用于创建一个复杂的对象,有两个作用:隔离了客户和产品的生产过程;负责控制产品对象的生产过程。

  • 使用建造者模式建造房子
package com.chen.Deom1;

public class DemoDemo {

    public static void main(String[] args) {
        SmallHouse smallHouse = new SmallHouse();
        Director director = new Director(smallHouse);
        director.start();
    }

}
//产品
class House{
    private String high;
    private String width;
}
//建造者
abstract class HouseBuilder {
    House house = new House();
    public abstract void builder1();
    public abstract void builder2();
    public abstract void builder3();

    public House startBuilder(){
        return house;
    }
}
//指挥者
class Director{
    private HouseBuilder houseBuilder = null;
    public Director(HouseBuilder houseBuilder){
        this.houseBuilder=houseBuilder;
    }
    public void start(){
        houseBuilder.builder1();
        houseBuilder.builder2();
        houseBuilder.builder3();
        houseBuilder.startBuilder();
    }
}
//具体的建造者
class SmallHouse extends HouseBuilder{


    @Override
    public void builder1() {
        System.out.println("小房子建造第一步");
    }

    @Override
    public void builder2() {
        System.out.println("小房子建造第二部");
    }

    @Override
    public void builder3() {
        System.out.println("小房子建造第二步");
    }

}

class BigHouse extends HouseBuilder{

    @Override
    public void builder1() {
        System.out.println("大房子建造第一步");
    }

    @Override
    public void builder2() {
        System.out.println("大房子建造第二步");
    }

    @Override
    public void builder3() {
        System.out.println("大房子建造第三步");
    }
}

建造者模式在JDK中的使用

StringBuilder stringBuilder = new StringBuilder();
AbstractStringBuilder
Appendable

  • AbstractStringBuilder充当一个Append方法的建造者
  • StringBuilder继承了AbstractStringBuilder抽象类,在这里充当了指挥者也是建造者

建造者模式的注意事项和细节

  1. 客户端不需要知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象
  2. 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便的替换具体建造者或者增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象
  3. 可以更加精细的控制产品的床架过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
  4. 增加新的具体建造者无需修改原有类库的代码,指挥者针对抽象建造者类编程,系统扩展方便,符合开闭原则
  5. 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
  6. 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得庞大,因此在这种情况下,需要考虑使用建造者模式

抽象工厂模式和建造者模式的区别

  • 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品有什么工厂生产即可,而建造者模式则是要求按照指定的流程建造产品,他的主要目的是通过组装零配件而产生一个新产品。

适配器模式

基本介绍

  • 适配器模式(Adapter Pattern)将某个类的接口转换为客户端期望的另一个接口表示,主要目的是兼容性,让原来因接口不匹配不能在一起工作的两个类可以协同工作,其别名为包装类(Wrapper)
  • 适配器模式属于结构型模式
  • 主要分为三类:类适配器模式,对象适配器模式,接口适配器模式

类适配器

类适配器模式的注意事项和细节

  • Java是单继承机制,所以类适配器需要继承被适配类,这一点算是一个缺点,因为这要求dst必须是接口,有一定的局限性
  • 被适配类的方法在适配器类中都会暴露出来,也增加了使用的成本
  • 由于其继承了被适配器类,所以他可以根据需求重写被适配器类的方法,使得适配器类更加灵活

使用类适配器模式模拟电源适配器

package com.chen.Deom1;

public class Demo111 {

    public static void main(String[] args) {

        Voltage5vAdapter voltage5vAdapter = new Voltage5vAdapter();
        RealmePhone realmePhone = new RealmePhone();
        realmePhone.changing(voltage5vAdapter);

    }
}

//被适配的220V电源
class Voltage220v {

    private int voltage = 220;
    //对外提供220V电源
    public int getVoltage220v(){
        return voltage;
    }

}
//适配接口
interface Voltage5v {
    //提供5v电压
    int getVoltage5v();
}
//适配器类
class Voltage5vAdapter extends Voltage220v implements Voltage5v {

    @Override
    public int getVoltage5v() {
        return getVoltage220v()/44;
    }
}
//适配器使用者手机
class RealmePhone {
    //充电
    public void changing(Voltage5v v){
        if (v.getVoltage5v()==5) System.out.println("充电成功");
    }
}

对象适配器

基本介绍

  • 思路和类适配器相同,只是将适配器类的继承改为组合被适配器类,以解决兼容性问题。
  • 根据合成复用原则,在系统中尽量使用关联关系来替代继承关系
  • 对象适配器模式是适配器模式常用的一种

对象适配器的注意事项和细节

  • 使用成本更低,更灵活

使用对象适配器模式模拟电源充电器

package com.chen.Deom1;

public class Demo111 {

    public static void main(String[] args) {

        Voltage5vAdapter voltage5vAdapter = new Voltage5vAdapter(new Voltage220v());
        RealmePhone realmePhone = new RealmePhone();
        realmePhone.changing(voltage5vAdapter);

    }
}

//被适配的220V电源
class Voltage220v {

    private int voltage = 220;
    //对外提供220V电源
    public int getVoltage220v(){
        return voltage;
    }

}
//适配接口
interface Voltage5v {
    //提供5v电压
    int getVoltage5v();
}
//适配器类
class Voltage5vAdapter implements Voltage5v {

    private Voltage220v voltage220v = null;
    public Voltage5vAdapter(Voltage220v voltage220v){
        this.voltage220v = voltage220v;
    }

    @Override
    public int getVoltage5v() {
        if (voltage220v!=null){
            return voltage220v.getVoltage220v() / 44;
        }
        return 0;
    }
}
//适配器使用者手机
class RealmePhone {
    //充电
    public void changing(Voltage5v v){
        if (v.getVoltage5v()==5) System.out.println("充电成功");
    }
}

接口适配器模式

基本介绍

  • 接口适配器模式也叫缺省适配器模式
  • 当不需要全部实现接口提供的方法时候,可先设计一个抽象类实现接口,并为该接口中的每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择的覆盖父类的某些方法来实现需求
  • 接口适配器模式适用于一个接口不想使用其所用的方法的情况

代码实现

package com.chen.Deom1;

public class Demo111 {

    public static void main(String[] args) {
        new CltClient(new CltAdapter() {
            @Override
            public void m1() {
                super.m1();
            }
        });
    }
}
//接口
interface Clt {
    void m1();
    void m2();
    void m3();
    void m4();
}

//接口适配器
abstract class CltAdapter implements Clt{
    @Override
    public void m1() {

    }

    @Override
    public void m2() {

    }

    @Override
    public void m3() {

    }

    @Override
    public void m4() {

    }
}
class CltClient {
    private CltAdapter cltAdapter = null;
    public CltClient(CltAdapter cltAdapter){
        this.cltAdapter = cltAdapter;
    }
}

SpringMVC中的HandlerAdapter适配器

  • Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类
  • 适配器代替Controller执行相应的方法,Controller本身不执行方法
  • 扩展Controller时候,只需要增加一个适配器类就完成了SpringMVC的扩展

适配器模式的注意事项和细节

  • 三种命名方式,是根据被适配者是以怎样的形式给到适配器的
  • 类适配器:以类的形式给到,继承被适配的类
  • 对象适配器:在适配器中聚合或者组合被适配的类
  • 接口适配器:适配器实现对应的被适配的接口
  • Adapter模式最大的作用还是将原来不兼容的接口融合在一起工作
  • 在实际开发中,实现起来不拘泥与上面的三种经典实现

桥接模式

案例引入

image-20240527102924049

image-20240527102941814

问题

  1. 扩展性问题(类爆炸),如果我们在增加手机的样式(旋转式),就需要增加各个品牌手机的类,同样如果我们增加一个手机品牌,也要各个手机样式类下增加。
  2. 违反了单一职责原则,当我们增加手机样式时候,要同时增加所有品牌的手机,这样增加了代码维护成本
  3. 解决方案:使用桥接模式

桥接模式基本介绍

  1. 桥接模式是指:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变。
  2. 是一种结构型设计模式
  3. Bridge模式基于类的最小设计原则,通过使用封装,聚合以及继承等行为让不同的类承担不同的职责。他的主要特点是把抽象(Abstraction)与行为实现(Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展

基于桥接模式实现例子

  • UML图

    image-20240527122141634

  • 可以实现在任意时间跑步之前的任何准备工作

    1. 充当桥梁的抽象类:AbstractRun

      
      public abstract class AbstractRun {
      
          /**
           * 实现效果:在跑步之前可以选择准备工作,可以实现任意时间的跑步和准备工作的组合
           */
      
          private PrepareBeforeRun prepareBeforeRun;
      
          public AbstractRun(PrepareBeforeRun prepareBeforeRun){
      
              this.prepareBeforeRun = prepareBeforeRun;
      
          }
      
          protected void run() {
              // 做准备工作
              prepareBeforeRun.prepare();
      
          }
      
      }
      
    2. 各种准备工作的抽象接口

      public interface PrepareBeforeRun {
      
          /**
           * 跑步之前的准备工作
           */
          void prepare();
      
      }
      
    3. 准备工作的其中一种,还可以进行扩展

      public class HaveBreakFastImpl implements PrepareBeforeRun {
      
          @Override
          public void prepare() {
              System.out.println("准备早餐");
          }
      
      }
      
    4. 跑步时间的其中一种,还可以扩展

      public class EveningRun extends AbstractRun {
      
      
          public EveningRun(PrepareBeforeRun prepareBeforeRun) {
              super(prepareBeforeRun);
          }
      
          @Override
          protected void run() {
              System.out.println("晚上跑步之前需要->:");
              super.run();
          }
      
      }
      

在jdbc中用到了桥接模式(不是太标准)

  • 因为作为桥梁的DriverManager不是一个抽象类,也没有子类实现

UML图

image-20240527123859188

桥接模式的注意事项和细节

  1. 实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性,让抽象部分和实现部分独立开来,这有助于系统进行分层设计,从而产生更好的结构化系统
  2. 对于系统的高层部分,只需要知道抽象部分和实现部分的接口即可,其他的部分由具体的业务来完成
  3. 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本
  4. 桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计和编程
  5. 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围有一定的局限性,即需要有这样的应用场景

桥接模式其他应用场景

  1. 对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用
  2. 常见的应用场景:
    • JDBC驱动程序
    • 银行转账系统
      • 转账分类:网上转账,柜台转账,AMT转账
      • 转账用户类型:普通用户,银卡用户,金卡用户
    • 消息管理:
      • 消息类型:即时消息,延时消息
      • 消息分类:手机短信,邮件短信,QQ消息...

装饰者模式

装饰者模式的定义

  1. 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,他比继承更有弹性,装饰者模式也体现了开闭原则(ocp)

装饰者模式解决订单配料问题

  • 可以使咖啡主体和配料进行任意搭配,进行订单的费用计算

image-20240527162159360

  1. 商品饮料抽象类:Drink

    public abstract class Decker extends Drink {
    
        private Drink drink;
    
        public Decker(Drink drink) {
            this.drink = drink;
        }
    
        @Override
        public double getPrice() {
            return super.getPrice() + drink.getPrice();
        }
    
        @Override
        public String getDesc() {
            return super.getDesc() + "," + drink.getDesc();
        }
    
    }
    
  2. 饮料的再抽象:Coffee,提高扩展性

    public abstract class Coffee extends Drink{
    
        public void setPrice(double price){
            super.setPrice(price);
        }
    
        public void setDesc(String desc){
            super.setDesc(desc+"咖啡");
        }
    
    }
    
  3. 咖啡的具体实现:CoffeeOne

    public class CoffeeOne extends Coffee {
    
        public CoffeeOne() {
            super.setDesc("一号");
            super.setPrice(22.0f);
        }
    
    }
    
  4. 使用配料进行装饰的抽象类:Decker

    public abstract class Decker extends Drink {
    
        private Drink drink;
    
        public Decker(Drink drink) {
            this.drink = drink;
        }
    
        @Override
        public double getPrice() {
            return super.getPrice() + drink.getPrice();
        }
    
        @Override
        public String getDesc() {
            return super.getDesc() + "," + drink.getDesc();
        }
    
    }
    
  5. 具体的配料实现:GrainOne

    public class GrainOne extends Decker {
    
        public GrainOne(Drink drink) {
            super(drink);
            super.setDesc("一号配料");
            super.setPrice(3.0f);
        }
    
        public String getDesc() {
            return super.getDesc();
        }
    
    }
    
  6. 测试:一杯咖啡喝两个配料的订单计算

    public class Starter {
    
        public static void main(String[] args) {
    
            // 一杯型号一的咖啡喝型号一的配料
            Drink order = new CoffeeOne();
            order = new GrainOne(order);
            order = new GrainOne(order);
            System.out.println(order.getDesc() + "花费" + order.getPrice());
    
        }
    
    }
    

    image-20240527162848190

java中的IO流用到的装饰者模式

image-20240527164425552

组合模式

组合模式基本介绍

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

image-20240528000058983

使用组合模式解决具有组织结构的业务

  • 实现一个抽象类对各个实现类的操纵,也就是上级对下级的管理
  1. 定义一个包含所有组织的抽象类的共同操作方法及其定义:AbstractOrganization

    • 可以使普通类,抽象类,接口
    public abstract class AbstractOrganization {
    
        private String name;
        private String desc;
    
        public AbstractOrganization(String name, String desc) {
            this.name = name;
            this.desc = desc;
        }
    
        protected String getName() {
            return this.name;
        }
    
        protected String getDesc() {
            return this.desc;
        }
    
        protected void add(AbstractOrganization organization) {};
    
        protected void remove(AbstractOrganization organization) {};
    
        public abstract void print();
    
    }
    
  2. 将各个组织以平级的方式实现,都继承AbstractOrganization

    • 学校

      public class University extends AbstractOrganization {
      
          private final List<AbstractOrganization> colleges = new ArrayList<>();
      
          public University(String name, String desc) {
              super(name, desc);
          }
      
          @Override
          protected void add(AbstractOrganization organization) {
      
              super.add(organization);
              colleges.add(organization);
      
          }
      
          @Override
          protected void remove(AbstractOrganization organization) {
      
              super.add(organization);
              colleges.remove(organization);
      
          }
      
          @Override
          public void print() {
      
              System.out.println("---"+getName());
      
              for (AbstractOrganization college : colleges) {
                  college.print();
              }
      
          }
      
      }
      
    • 学院

      public class College extends AbstractOrganization {
      
          private final List<AbstractOrganization> departments = new ArrayList<>();
      
          public College(String name, String desc) {
              super(name, desc);
          }
      
          @Override
          protected void add(AbstractOrganization organization) {
              super.add(organization);
              departments.add(organization);
          }
      
          @Override
          protected void remove(AbstractOrganization organization) {
              super.remove(organization);
              departments.remove(organization);
          }
      
          @Override
          public void print() {
      
              System.out.println("--"+getName());
              for (AbstractOrganization department : departments) {
                  department.print();
              }
      
          }
      
      }
      
    • 院系

      public class Department extends AbstractOrganization {
      
          public Department(String name, String desc) {
              super(name, desc);
          }
      
          @Override
          public void print() {
              System.out.println("-" + getName());
          }
      
      }
      
  3. 测试

    public class Starter {
    
        public static void main(String[] args) {
    
            // 定义大学
            AbstractOrganization university = new University("哈尔滨工业大学", "世界顶尖高校");
    
            // 定义学院
            AbstractOrganization var1 = new College("计算机学院", "计算机学院");
            AbstractOrganization var2 = new College("信息工程学院", "信息工程学院");
    
            //定义系
            AbstractOrganization var3 = new Department("软件工程", "软件工程");
            AbstractOrganization var4 = new Department("数字媒体", "数字媒体");
            var1.add(var3);
            var2.add(var4);
    
            university.add(var1);
            university.add(var2);
    
            university.print();
    
        }
    
    }
    
    • 实现了组织结构的打印及其对子部门的管理

    image-20240527235923140

JDK中的Map使用到了组合模式

image-20240528001254008

外观模式

外观模式的基本介绍

  1. 外观模式,也叫过程模式:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用

  2. 外观模式通过定义一个一致的接口,用于屏蔽内部子系统的细节,使得调节端只需要跟这个接口发生调用,而无需关心这个子系统的内部实现细节

    image-20240528101348385

使用外观模式解决人的一生

  1. 定义人生的各个阶段:LifecyleBorth,LifecyleLive,LifecyleDead

    public class LifecyleBorth {
    
        private static final LifecyleBorth instance = new LifecyleBorth();
    
        public static LifecyleBorth getInstance() {
            return instance;
        }
    
        public void borth() {
            System.out.println("人的一生----出生");
        }
    
    }
    
    public class LifecyleLive {
    
        private static final LifecyleLive instance = new LifecyleLive();
    
        public static LifecyleLive getInstance() {
            return instance;
        }
    
        public void live() {
            System.out.println("人的一生----生存");
        }
    
    }
    
    public class LifecyleDead {
    
        private static final LifecyleDead instance = new LifecyleDead();
    
        public static LifecyleDead getInstance() {
            return instance;
        }
    
        public void dead() {
            System.out.println("人的一生----死亡");
        }
    
    }
    
  2. 定义一个外观类统一调用:SystemFacade

    public class SystemFacade {
    
        /**
         * 外观类
         */
        private static final LifecyleBorth lifecyleBorth = LifecyleBorth.getInstance();
        private static final LifecyleLive lifecyleLive = LifecyleLive.getInstance();
        private static final LifecyleDead lifecyleDead = LifecyleDead.getInstance();
    
    
        public void life() {
    
            lifecyleBorth.borth();
            lifecyleLive.live();
            lifecyleDead.dead();
    
        }
    
    }
    
  3. 测试

    public class Starter {
    
        public static void main(String[] args) {
    
            SystemFacade systemFacade = new SystemFacade();
            systemFacade.life();
    
        }
    
    }
    

Mybatis中使用到的外观模式

image-20240528110252717

享元模式

享元模式基本介绍

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

享元模式的内部状态和外部状态

  • 享元模式提出来两个要求:细粒度和共享对象,这里就涉及到内部状态和外部状态了,即将对象的信息分为两个部分:内部状态和外部状态
  • 内部状态:指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变
  • 外部状态:指对象得以依赖的一个标记,是随环境改变而改变的,不可共享的状态
  • 比如:棋子的颜色和坐标,颜色就是内部状态,坐标就是外部状态

使用享元模式制作连接池

  • 实现了连接的复用
  1. 定义连接抽象:MyConnection

    public interface MyConnection {
    
        void open(String threadName);
    
        Boolean isInUse();
    
        void setInUse(Boolean inUse);
    
        void close();
    
    }
    
  2. 定义实现类:MyConnectionImpl

    public class MyConnectionImpl implements MyConnection {
    
        private String name;
    
        private Boolean isInUse = false;
    
        public MyConnectionImpl(String name) {
            this.name = name;
        }
    
        public Boolean isInUse() {
            return isInUse;
        }
    
        @Override
        public void setInUse(Boolean inUse) {
            this.isInUse = inUse;
        }
    
        @Override
        public void open(String threadName) {
            System.out.println("Connection:" + name + "正在被" + threadName + "使用");
        }
    
        @Override
        public void close() {
            this.isInUse = false;
            System.out.println("Connection:" + name + "被释放");
        }
    
    }
    
  3. 定义连接工厂

    public class ConnectionFactory {
    
        private static final List<MyConnection> pools = new ArrayList<>();
    
        public ConnectionFactory() {
            // 初始化2个连接
            pools.add(new MyConnectionImpl("连接1"));
            pools.add(new MyConnectionImpl("连接2"));
        }
    
        public synchronized MyConnection getConnection() {
            for (MyConnection connection : pools) {
                // 获取空闲的连接
                if (!connection.isInUse()) {
                    connection.setInUse(true);
                    return connection;
                }
            }
            // 重新获取
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getConnection();
        }
    
    }
    
  4. 测试

    public class Starter {
    
        public static void main(String[] args) throws Exception {
    
            ConnectionFactory connectionFactory = new ConnectionFactory();
    
    
            Thread thread1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    MyConnection connection = connectionFactory.getConnection();
                    // 使用2s
                    try {
                        connection.open("线程1");
                        Thread.sleep(2000);
                        connection.close();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            Thread thread2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    MyConnection connection = connectionFactory.getConnection();
                    try {
                        connection.open("线程2");
                        Thread.sleep(3000);
                        connection.close();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            Thread thread3 = new Thread(new Runnable() {
                @Override
                public void run() {
                    MyConnection connection = connectionFactory.getConnection();
                    try {
                        connection.open("线程3");
                        Thread.sleep(5000);
                        connection.close();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            thread1.start();
            thread2.start();
            thread3.start();
    
        }
    
    }
    
    • 实现了三个线程复用两个连接

    image-20240528131020921

JDK中的Integer的valueOf方法使用到了享元模式

// Integer享元模式支持的范围-128 - 127 ,否则直接创建新的对象
Integer one = Integer.valueOf(128);
Integer two = Integer.valueOf(128);
System.out.println(one == two); // false

Integer three = Integer.valueOf(127);
Integer four = Integer.valueOf(127);
System.out.println(three == four); // true

代理模式

代理模式介绍

  1. 代理模式:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象,这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能
  2. 被代理对的对象可以是远程对象,创建开销大的对象或者需要安全控制的对象
  3. 代理模式有不同的形式,主要有三种:静态代理,动态代理,Cglib代理
    • 动态代理:也叫JDK代理,接口代理
    • Cglib代理:可以在内存动态的创建对象,而不需要实现接口,他是属于动态代理的范畴

静态代理

静态代理模式的基本介绍
  • 静态代理在使用时候,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类

  • 静态代理示例:

    1. 接口:

      public interface IHelloDao {
      
          void sayHello();
      
      }
      
    2. 实现类

      public class IHelloDaoImpl implements IHelloDao{
      
          @Override
          public void sayHello() {
      
              System.out.println("小张你好");
      
          }
      
      }
      
    3. 代理类

      public class IHelloDaoImplProxy implements IHelloDao{
      
          private IHelloDao target;
      
          public IHelloDaoImplProxy(IHelloDao iHelloDao) {
              this.target = iHelloDao;
          }
      
          @Override
          public void sayHello() {
      
              System.out.println("小明你好");
              target.sayHello();
              System.out.println("两人成为好朋友");
      
          }
          
      }
      
    4. 测试

      public class Starter {
      
          public static void main(String[] args) {
      
              IHelloDao target = new IHelloDaoImpl();
      
              IHelloDao proxy = new IHelloDaoImplProxy(target);
      
              proxy.sayHello();
      
          }
      
      }
      
      • 成功实现了代理

      image-20240529110328201

静态代理的优缺点
  1. 优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
  2. 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类;一旦增加接口方法,目标对象和代理对象都要维护

动态代理

动态代理的基本介绍
  1. 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
  2. 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
  3. 动态代理也叫做:JDK代理,接口代理
  • 动态代理示例:

    1. 定义接口

      public interface IHelloDao {
      
          void sayHello();
      
      }
      
    2. 定义实现类

      public class IHelloDaoImpl implements IHelloDao{
      
      
          @Override
          public void sayHello() {
              System.out.println("Hello,Proxy");
          }
      
      }
      
    3. 定义生成代理对象的工厂

      public class DynamicFactory {
      
      
          public static Object getProxyInstance(Object target) {
              /**
               * 第一个参数:目标对象使用的类加载器
               * 第二个参数:目标对象实现的接口
               * 第三个参数:InvocationHandler对象,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行的目标对象方法作为参数传入
               */
              return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                      target.getClass().getInterfaces(),
                      (proxy, method, args) -> {
                          System.out.println("before method");
      
                          Object invoke = method.invoke(target, args);
      
                          System.out.println("after method");
                          return invoke;
                      }
              );
      
          }
      
      }
      
    4. 测试

      public class Starter {
      
          public static void main(String[] args) {
      
              IHelloDao target = new IHelloDaoImpl();
              IHelloDao proxy = (IHelloDao) DynamicFactory.getProxyInstance(target);
              proxy.sayHello();
      
          }
      
      }
      
      • 成功实现动态代理

      image-20240529115305760

Cglib代理

Cglib代理模式的基本介绍
  1. 静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可以使用目标对象子类来实现代理,这就是Cglib代理
  2. Cglib代理也叫做子类代理,他是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib归属于动态代理
  3. Cglib是一个强大的高性能的代码生成包,他可以在运行期间扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如Spirng AOP,实现方法拦截
  4. 在AOP编程中如何选择代理模式:
    • 目标对象需要实现接口,使用JDK代理
    • 目标对象不需要实现接口,用Cglib代理
  5. Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类
  • 注意点:使用Cglib代理的目标类不能为final修饰,否则报错;目标对象的方法如果被final或者static修饰,那么该方法就不会被拦截(增强)
使用Cglib实现代理
  1. 导入相关jar包

    image-20240529125613654

  2. 目标类

    public class IHelloDaoImpl {
    
        public void sayHello() {
            System.out.println("Hello,Cglib");
        }
    
    }
    
  3. Cglib工厂

    public class CglibFactory {
    
        public Object getProxy(Object target) {
    
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(new MethodInterceptor() {
                /**
                 *
                 * @param proxy 生成的代理子类
                 * @param method 目标方法
                 * @param args 目标方法参数
                 * @param methodProxy 生成的代理方法
                 * @return
                 * @throws Throwable
                 */
                @Override
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    
                    System.out.println("生成的代理对象:" + proxy.getClass());
                    System.out.println("生成的代理方法:" + methodProxy.getClass());
                    System.out.println("before method");
    
                    Object invoke = method.invoke(target, args);
    
                    System.out.println("after method");
                    return invoke;
    
                }
            });
            return enhancer.create();
    
        }
    
    }
    
  4. 测试

    public class Starter {
    
        public static void main(String[] args) {
    
            IHelloDaoImpl target = new IHelloDaoImpl();
            IHelloDaoImpl proxy = (IHelloDaoImpl) new  CglibFactory().getProxy(target);
            proxy.sayHello();
    
        }
    
    }
    
    • 成功使用Cglib实现代理

    image-20240529125840412

  • 注意点:使用cglib代理如果使用Java8以上,需要加上运行参数--add-opens java.base/java.lang=ALL-UNNAMED

代理模式的变体

  1. 防火墙代理:内网通过代理穿透防火墙,实现对公网的访问
  2. 缓存代理:比如:当请求图片文件等资源的时候,先到缓存代理取,如果取到资源则返回,否则到公网或者数据库取,然后再进行缓存
  3. 远程代理:远程对象的本地代表,通过他可以把远程对象当本地对象来调用,远程代理通过网络和真正的远程对象沟通信息
  4. 同步代理:主要使用在多线程编程中,完成多线程间同步工作

模版方法模式

模版方法的基本介绍

  • 模版方法模式,又叫模版模式,在一个抽象类公开定义了执行他的方法的模版,他的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行

  • 简单说,模版方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定的步骤

  • 这种类型的设计模式属于行为型模式

  • 模版方法示例:

    1. 定义模版方法抽象类

      public abstract class AbstractTemplate {
      
          public final void templateMethod() {
      
              method1();
              method2();
              method3();
              method4();
      
          }
      
          public void method1() {
              System.out.println("第一步(相同的步骤)");
          }
      
          public void method2() {
              System.out.println("第二步(相同的步骤)");
          }
      
          public abstract void method3();
      
          public void method4() {
              System.out.println("第四步(相同的步骤)");
          }
      
      }
      
    2. 具体的子类:

      public class MyTemplate extends AbstractTemplate{
          
          @Override
          public void method3() {
              System.out.println("MyTemplate method3");
          }
      
      }
      
      public class YouTemplate extends AbstractTemplate{
      
          @Override
          public void method3() {
              System.out.println("YouTemplate method3");
          }
      
      }
      
    3. 测试:

      public class Starter {
      
          public static void main(String[] args) {
      
              AbstractTemplate myTemplate = new MyTemplate();
              myTemplate.templateMethod();
      
              System.out.println("-------------------------");
      
              AbstractTemplate youTemplate = new YouTemplate();
              youTemplate.templateMethod();
      
          }
      
      }
      
      • 成功实现模版方法

      image-20240529135143015

模版方法中的钩子方法

介绍:

  • 在模版方法模式的父类中,我们可以定义一个方法,他默认不做任何事情,子类可以视情况要不要覆盖它,该方法称为钩子
  • 钩子方法只需要定义在抽象类里面,并且为空实现即可,子类可以选择是否覆盖他

在Spring中使用到了模版方法:Refresh()方法

命令模式

命令模式的基本介绍

  1. 命令模式:在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接受者是谁,也不知道被请求的操作是哪个,我们只需要在程序运行时候指定具体的请求接受者即可,此时,可以使用命令模式来进行设计
  2. 命令模式使得请求发送者与请求接受者彼此之间的耦合消除,让对象之间的调用关系更加灵活,实现解耦
  3. 在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求,同时命令模式也支持可撤销的操作

责任链模式

责任链模式基本介绍

  1. 职责链模式又叫责任链模式,为请求创建了一个接收者对象的链,这种模式对请求的发送者和接收者进行解耦
  2. 责任链模式通常每个接收者都包含对另一个接收者的引用,如果一个对象不能处理该请求,那么他会把相同的请求传给下一个接受者,以此类推
  3. 责任链模式使得多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止
  4. 这种类型的设计模式属于行为型模式
  • 责任链模式示例:

    1. 封装请求:

      public class OaRequest {
      
          private String role;
      
          public OaRequest(String role) {
              this.role = role;
          }
      
          public String getRole() {
              return this.role;
          }
      
      }
      
    2. 定义请求处理的抽象类:

      public abstract class OaRequestHandler {
      
          protected OaRequestHandler oaRequestHandler;
      
          public void setOaRequestHandler(OaRequestHandler handler) {
              this.oaRequestHandler = handler;
          }
      
          public abstract void handlerRequest(OaRequest request);
      
      }
      
    3. 实现几个处理人

      public class OneHandler extends OaRequestHandler {
      
          @Override
          public void handlerRequest(OaRequest request) {
      
              if ("one".equals(request.getRole())) {
                  System.out.println("请求被one处理");
              }else {
                  super.oaRequestHandler.handlerRequest(request);
              }
      
          }
      
      }
      
      public class TwoHandler extends OaRequestHandler {
      
          @Override
          public void handlerRequest(OaRequest request) {
              if ("two".equals(request.getRole())) {
                  System.out.println("请求被two处理");
              }else{
                  super.oaRequestHandler.handlerRequest(request);
              }
          }
      
      }
      
    4. 测试:

      public class Starter {
      
          public static void main(String[] args) {
      
              OaRequest request = new OaRequest("two");
      
              OneHandler oneHandler = new OneHandler();
              TwoHandler twoHandler = new TwoHandler();
              twoHandler.setOaRequestHandler(oneHandler);
              oneHandler.setOaRequestHandler(twoHandler);
      
              twoHandler.handlerRequest(request);
              
          }
      
      }
      
      • 成功实现链式责任链模式处理请求

访问者模式

访问者模式基本介绍

  • 访问者模式(Visitor Pattern), 封装一些作用于某种数据结构的各元素的操作,他可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

  • 主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题

  • 访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口

  • 访问者模式主要应用场景:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作污染这些对象的类,可以选用访问者模式解决

  • 访问者模式示例:

    1. 定义一个访问者抽象类:

      public abstract class AbstractVisitor {
      
          public void visitElementOne(ElementOne element){};
      
          public void visitElementTwo(ElementTwo elementTwo){};
      
      }
      
    2. 实现对元素一的访问者

      public class ElementOneVisitor extends AbstractVisitor {
      
          @Override
          public void visitElementOne(ElementOne element) {
              System.out.println("VisitorOne可以访问到->" + element.getName());
          }
      
      }
      
    3. 定义元素抽象类:

      public abstract class Element {
      
          private String name;
          private String age;
      
          public Element(String name, String age) {
              this.name = name;
              this.age = age;
          }
      
          public String getName() {
              return name;
          }
      
          public String getAge(){
              return age;
          }
      
          public abstract void accept(AbstractVisitor visitor);
      
      }
      
      
    4. 定义元素一

      public class ElementOne extends Element{
      
          public ElementOne(String name, String age) {
              super(name, age);
          }
      
          @Override
          public void accept(AbstractVisitor visitor) {
              visitor.visitElementOne(this);
          }
      
      }
      
      • 如果是访问者一进来访问,则可以通过在访问者一中定义的方法参数进行匹配,从而访问当相应的元素
    5. 定义管理元素的数据类

      public class ObjectStructor {
      
          private List<Element> data = new ArrayList<>();
      
          public void attach(Element element) {
              data.add(element);
          }
      
          public void detach(Element element) {
              data.remove(element);
          }
      
          // 提供访问者匹配元素,进而访问元素
          public void display(AbstractVisitor visitor) {
              for (Element datum : data) {
                  datum.accept(visitor);
              }
          }
      
      }
      
    6. 测试:

      public class Starter {
      
          public static void main(String[] args) {
      
              ObjectStructor objectStructor = new ObjectStructor();
              // 添加元素
              objectStructor.attach(new ElementOne("元素1", "22"));
              objectStructor.attach(new ElementTwo("元素2", "33"));
      
              objectStructor.display(new ElementOneVisitor());
      
          }
      
      }
      
      • 在众多元素中访问者一只能访问元素1

      image-20240529231310194

双分派

  • 是指不管元素怎么变化,访问者怎么变化,都能找到对应的访问者访问对应的元素
  • 对元素的操作的执行取决于访问者访问的元素和元素能接受的访问者

访问者模式的注意事项和细节

优点:

  1. 访问者模式符合单一职责原则,让程序具有优秀的扩展性,灵活性非常高
  2. 访问者模式可以对功能进行统一,可以做报表,UI,拦截器与过滤器,适用于数据结构相对稳定的系统

缺点:

  1. 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这就是迪米特法则所不建议的,这样就造成了具体元素变更比较困难
  2. 违背了依赖倒转原则,访问者依赖的是具体元素,而不是抽象元素
  3. 因此,如果一个系统有比较稳定的数据结构(指元素的属性等),又有经常变化的功能需求,那么访问者模式就是比较适合的

迭代器模式

迭代器模式基本介绍

  1. 迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式
  2. 如果我们的集合元素是用不同的方式实现的,有数组,还有java的集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决
  3. 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示。
  • 迭代器示例:

    1. 定义一个字符串迭代器和集合迭代器

      public class ListIterator implements Iterator {
      
          private List list = null;
          private int index = 0;
      
          public ListIterator(List list) {
              this.list = list;
          }
      
          @Override
          public boolean hasNext() {
      
              if (index < list.size() && list.get(index) != null) {
                  return true;
              }
      
              return false;
          }
      
          @Override
          public Object next() {
              return list.get(index++);
          }
      
      }
      
      public class StrArrIterator implements Iterator {
      
          private String[] strArr = null;
          private int index = 0;
      
          public StrArrIterator(String[] strArr) {
              this.strArr = strArr;
          }
      
          @Override
          public boolean hasNext() {
      
              if (index < strArr.length && strArr[index] != null) {
                  return true;
              }
      
              return false;
          }
      
          @Override
          public Object next() {
      
              return strArr[index++];
      
          }
      
      }
      
    2. 自定义数据接口:用于为子类提供遍历的方法

      public interface MyData {
      
          public Iterator createIterator();
      
      }
      
    3. 定义字符串数据和集合数据

      public class DataStrArr implements MyData{
      
          private String[] strArr = null;
      
          public DataStrArr(String[] strArr) {
              this.strArr = strArr;
          }
      
          @Override
          public Iterator createIterator() {
              return new StrArrIterator(strArr);
          }
      
      }
      
      public class DataList implements MyData{
      
          private List dataList = null;
      
          public DataList(List dataList) {
              this.dataList = dataList;
          }
      
          @Override
          public Iterator createIterator() {
              return new ListIterator(dataList);
          }
      
      }
      
    4. 测试:

      public class Starter {
      
      
          public static void main(String[] args) {
      
              DataStrArr dataStrArr = new DataStrArr(new String[]{"name1","name2","name3"});
              Iterator iterator = dataStrArr.createIterator();
              while (iterator.hasNext()) {
                  String next = (String) iterator.next();
                  System.out.println(next);
              }
      
              DataList dataList = new DataList(Arrays.asList("name1", "name2", "name3"));
              Iterator iterator1 = dataList.createIterator();
              while(iterator1.hasNext()) {
      
                  String next = (String) iterator1.next();
                  System.out.println(next);
      
              }
              
          }
      
      }
      
      • 成功遍历

      image-20240530122627282

观察者模式

观察者模式基本介绍

  • 对象之间多对一依赖的一种设计方案,被依赖的对象为Subject,依赖的对象为Observer,Subject通知Observer变化,比如这里的气象局是Subject,第三方数据使用者是Observer

  • 观察者模式示例:

    1. 定义观察者观察接口:

      public interface Subject {
      
          void registerObserver(Observer observer);
      
          void removeObserver(Observer observer);
      
          void notifyObservers();
      
      }
      
    2. 定义观察者接口:

      public interface Observer {
      
          void update(String message);
      
      }
      
    3. 定义消息服务的观察者的观察对象:

      public class MessageSubject implements Subject {
      
          private List<Observer> observers = null;
          private String message = null;
      
          public MessageSubject() {
              observers = new ArrayList<>();
          }
      
          public void setMessage(String message) {
              this.message = message;
          }
      
          @Override
          public void registerObserver(Observer observer) {
              observers.add(observer);
          }
      
          @Override
          public void removeObserver(Observer observer) {
              observers.remove(observer);
          }
      
          @Override
          public void notifyObservers() {
      
              Iterator<Observer> iterator = observers.iterator();
              while (iterator.hasNext()) {
      
                  Observer next = iterator.next();
                  next.update(message);
      
              }
          }
      
      }
      
    4. 定义观察者:UserOne,UserTwo

      public class UserOne implements Observer {
          
          @Override
          public void update(String message) {
              System.out.println("接收到更新的消息:" + message);
          }
      
      }
      
      public class UserTwo implements Observer {
          
          @Override
          public void update(String message) {
              System.out.println("接收到更新的消息:" + message);
          }
          
      }
      
    5. 测试:

      public class Starter {
      
          public static void main(String[] args) {
      
              MessageSubject subject = new MessageSubject();
              subject.registerObserver(new UserOne());
              subject.registerObserver(new UserTwo());
      
              // 更新消息
              subject.setMessage("hello world");
              subject.notifyObservers();
      
              subject.setMessage("hello world version2");
              subject.notifyObservers();
      
          }
      
      }
      
      • 成功更新数据后通知观察者

      image-20240530134154774

观察者设计模式的好处

  1. 观察者模式,提供服务的一方会以集合的方式来管理自己的用户,包括注册,移除,通知消息变更
  2. 遵守了OCP原则

JDK的Observable接口即是观察者模式

中介者模式

中介者模式基本介绍

  1. 中介者模式(Mediator Pattern),用一个中介对象来封装一系列的对象交互。中介者使各个对象不需要显式的相互引用,从而使其耦合性松散,而且可以独立的改变他们之间的交互。
  2. 中介者模式属于行为型模式,使代码易于维护
  3. 比如MVC模式,C是M和V之间的中介者
  • 中介者模式示例:

    1. 定义中介者接口:

      public interface Meditor {
      
          void register(Student student);
      
          void unregister(Student student);
      
          void sendMessage(String studentName , Student student);
      
          String getMessage(String studentName);
      
      }
      
    2. 定义对接对象Student

      public interface Student {
      
          void sendMessage(String message);
      
      }
      
    3. 定义一个学生和客户端的消息中介者

      public class MessageMeditor implements Meditor{
      
          private Map<String, Student> studentMap;
      
          public MessageMeditor() {
              studentMap = new HashMap<>();
          }
      
          @Override
          public void register(Student student) {
              studentMap.put(student.getClass().getName(), student);
          }
      
          @Override
          public void unregister(Student student) {
              studentMap.remove(student.getClass().getName());
          }
      
          @Override
          public void sendMessage(String studentName, Student student) {
              studentMap.put(studentName, student);
          }
      
          @Override
          public String getMessage(String studentName) {
              StudentOne student = (StudentOne) studentMap.get(studentName);
              return student.getMessage();
          }
      
      }
      
    4. 定义一个学生:

      public class StudentOne implements Student{
      
          private Meditor meditor;
          private String message;
      
          public StudentOne(Meditor meditor) {
      
              this.meditor = meditor;
              meditor.register(this);
          }
      
          public String getMessage() {
              return message;
          }
      
          @Override
          public void sendMessage(String message) {
              this.message = message;
              meditor.sendMessage(this.getClass().getName(), this);
          }
      
      }
      
    5. 测试:

      public class Starter {
      
          public static void main(String[] args) {
      
              Meditor meditor = new MessageMeditor();
              StudentOne studentOne = new StudentOne(meditor);
              studentOne.sendMessage("Hello,World");
              String message = meditor.getMessage(studentOne.getClass().getName());
              System.out.println(message);
      
          }
      
      }
      

备忘录模式

备忘录模式基本介绍

  1. 备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态
  2. 备忘录模式属于行为型模式
  • 备忘录模式示例:

    1. 定义原始数据:

      public class Original {
      
          private String name;
          private int age;
          private int status;
      
      
          public Original(String name, int age, int status) {
      
              this.name = name;
              this.age = age;
              this.status = status;
      
          }
      
          public MementoTemp createTemp() {
              return new MementoTemp(name, age, status);
          }
      
          public void restore(MementoTemp mementoTemp) {
              this.name = mementoTemp.getName();
              this.age = mementoTemp.getAge();
              this.status = mementoTemp.getStatus();
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          public int getStatus() {
              return status;
          }
      
          public void setStatus(int status) {
              this.status = status;
          }
      
          @Override
          public String toString() {
              return "Original{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      ", status=" + status +
                      '}';
          }
      }
      
    2. 定义备份对象:

      public class MementoTemp {
      
          private String name;
          private int age;
          private int status;
      
      
          public MementoTemp(String name, int age, int status) {
      
              this.name = name;
              this.age = age;
              this.status = status;
      
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public int getAge() {
              return age;
          }
      
          public void setAge(int age) {
              this.age = age;
          }
      
          public int getStatus() {
              return status;
          }
      
          public void setStatus(int status) {
              this.status = status;
          }
      
          @Override
          public String toString() {
              return "Original{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      ", status=" + status +
                      '}';
          }
      
      }
      
    3. 定义管理各个状态的对象

      public class MementoList {
      
          private Map<Integer, MementoTemp> list = new HashMap<>();
      
          public void add(MementoTemp mementoTemp){
              list.put(mementoTemp.getStatus(), mementoTemp);
          }
      
          public void remove(MementoTemp mementoTemp){
              list.remove(mementoTemp.getStatus());
          }
      
          public MementoTemp getTemp(int status) {
              return list.get(status);
          }
      
      }
      
    4. 测试:

      public class Starter {
      
          public static void main(String[] args) {
      
      
              MementoList mementoList = new MementoList();
              Original status1 = new Original("陈浪涛", 22, 1);
              mementoList.add(status1.createTemp());
      
              Original status2 = new Original("陈浪涛", 22, 2);
              mementoList.add(status2.createTemp());
      
      
              Original status3 = new Original("陈浪涛", 22, 3);
              mementoList.add(status3.createTemp());
      
      
              System.out.println("现在的状态"+status3);
              System.out.println("恢复到状态1");
              status3.restore(mementoList.getTemp(1));
              System.out.println("恢复的状态"+status3);
      
          }
      
      }
      
      • 成功恢复到原来的任何状态

      image-20240531101145306

解释器模式

解释器模式基本介绍

  1. 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元在通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树,这里的词法分析器和语法分析器都可以看做是解释器
  2. 解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义他的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
  3. 应用场景:
    • 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
    • 一些重复出现的问题可以用一种简单的语言来表达
    • 一个简单语法需要解释的场景
    • 比如:编译器,表达式计算,正则表达式,机器人等

解释器模式的源码

  • Spring的SpelExpressionParser就使用到了解释器模式

image-20240531114214608

解释器模式的注意事项和细节

  1. 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
  2. 应用场景:编译器,运算表达式计算,正则表达式,机器人等
  3. 使用解释器可能带来的问题:解释器模式会引起类膨胀,解释器模式采用递归调用方法,将会导致调试非常复杂,效率可能降低

状态模式

状态模式的基本介绍

  1. 状态模式(State Pattern):他主要用来解决对象在多种状态转换时候,需要对外输出不同的行为的问题,状态和行为是一一对应的,状态之间可以相互转换
  2. 当一个对象的内在状态改变时候,允许改变其行为,这个对象看起来像是改变了其类
  • 状态模式示例:

    1. 定义管理状态的容器:

      public class Context {
      
          private int money;
          private Status currentStatus;
      
          private Status statusOne = new StatusOne("准备抽奖", this);
          private Status statusTwo = new StatusTwo("抽奖中", this);
          private Status statusThree = new StatusThree("中奖了", this);
          private Status statusFour = new StatusFour("钱不够", this);
      
          public Context(int money) {
              this.money = money;
              this.currentStatus = statusOne;
          }
      
          public void setMoney(int money) {
              this.money = money;
          }
      
          public int getMoney() {
              return money;
          }
      
          public Status getCurrentStatus() {
              return this.currentStatus;
          }
      
          // 活动开始
          public void prepare() {
              if (currentStatus.isEnabledActivity()){
                  currentStatus.minusMoney();
              }else{
                  this.currentStatus = statusFour;
              }
          }
      
          // 抽奖中
          public void inActivity() {
              this.currentStatus = statusTwo;
              this.currentStatus.inActivity();
              int number = (int) ((Math.random())*10);
              if (number == 1) {
                  this.currentStatus = statusThree;
                  this.currentStatus.getPrize();
              }else{
                  this.currentStatus = statusFour;
              }
      
          }
      
          public void result() {
              this.currentStatus.noPrice();
          }
      
      
      }
      
    2. 定义状态抽象类:

      public abstract class Status {
      
          private String name;
          private Context context;
      
          public Status(String name, Context context) {
              this.name = name;
              this.context = context;
          }
      
          // 每次抽奖都会扣除10块钱
          public void minusMoney() {
              System.out.println("==================");
              System.out.println("抽奖扣钱...");
              context.setMoney(context.getMoney() - 10);
              System.out.println("扣钱10块");
          }
      
          // 判断是否能继续抽奖
          public boolean isEnabledActivity(){
              return context.getMoney() > 0;
          }
      
          public void getPrize(){
              System.out.println("恭喜你中奖了");
          }
      
          public void inActivity() {
      
          }
      
          public abstract void noPrice();
      
      }
      
    3. 定义容器用到的四个状态:

      public class StatusOne extends Status {
      
          // 准备抽奖
      
          public StatusOne(String name, Context context) {
              super(name, context);
          }
      
          @Override
          public void noPrice() {
      
          }
      
      }
      
      public class StatusTwo extends Status {
      
      
          // 抽奖中
          public StatusTwo(String name, Context context) {
              super(name, context);
          }
      
          @Override
          public void noPrice() {
      
          }
      
          public void inActivity() {
              System.out.println("抽奖中");
          }
      
      }
      
      public class StatusThree extends Status {
      
      
          // 中奖了
          public StatusThree(String name, Context context) {
              super(name, context);
      
          }
      
          @Override
          public void noPrice() {
      
          }
      
      }
      
      public class StatusFour extends Status {
      
      
          // 钱不够了
          public StatusFour(String name, Context context) {
              super(name, context);
          }
      
          @Override
          public void noPrice() {
              System.out.println("没有抽中");
          }
      
      }
      
      
    4. 测试:

      public class Starter {
      
          public static void main(String[] args) {
      
              Context context = new Context(50);
      
      
              for (int i = 0; i < 10; i++) {
                  if (context.getCurrentStatus().isEnabledActivity()){
                      context.prepare();
                      context.inActivity();
                      context.result();
                  }
              }
      
          }
      
      }
      
      • 成功通过改变状态实现抽奖活动

      image-20240531140532729

借贷系统中使用到的状态模式

image-20240531141614314

状态模式的注意事项和细节

  1. 代码有很强的可读性,状态模式将每个状态的行为封装到对应的一个类中
  2. 方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时候都要判断当前是什么状态,不但会产生很多if-else语句,而且容易出错
  3. 符合开闭原则,容易增删状态
  4. 会产生很多类,每个状态都要一个对应的类,当状态过多的时候会产生很多类,加大维护难度
  5. 当一个事件或者对象有很多种状态时候,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式

策略模式

策略模式基本介绍

  • 策略模式(Strategy Pattern)中,定义算法族,分别分装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户

  • 这算法体现了几个设计原则,第一,把变化的代码从不变的代码中分离出来;第二,针对接口编程而不是具体类(定义了策略接口);第三,多用组合、聚合,少用继承(客户通过组合方式使用策略)

  • 策略模式示例:

    1. 定义一个抽象类:

      public abstract class AbstractDuck {
      
          protected FlyBehavior flyBehavior;
      
          public void fly() {
              if (flyBehavior != null) {
                  flyBehavior.fly();
              }
          }
      
      }
      
    2. 定义上面的抽象类会用到的策略接口

      public interface FlyBehavior {
      
          void fly();
      
      }
      
    3. 实现行为接口

      public class FlyBehaviorOne implements FlyBehavior{
      
      
          @Override
          public void fly() {
              System.out.println("不会飞翔");
          }
      
      
      }
      
      
      public class FlyBehaviorTwo implements FlyBehavior{
      
      
          @Override
          public void fly() {
              System.out.println("我不会飞翔");
          }
      
      }
      
    4. 实现继承抽象类:

      public class NoFlyDuck extends AbstractDuck{
      
          public NoFlyDuck() {
              flyBehavior = new FlyBehaviorTwo();
          }
      
      }
      
      public class CanFlyDuck extends AbstractDuck{
      
          public CanFlyDuck() {
              flyBehavior = new FlyBehaviorOne();
          }
      
      }
      
      
    5. 测试

      public class Starter {
      
          public static void main(String[] args) {
      
              AbstractDuck noFly = new NoFlyDuck();
              noFly.fly();
      
              AbstractDuck canFly = new CanFlyDuck();
              canFly.fly();
      
      
          }
      
      }
      
      • 成功将鸭子的飞翔行为抽离成接口,单独实现再组合进去,避免继承带来的问题

      image-20240531180552541

Arrys实现了策略模式

策略模式的注意事项和细节

  1. 策略模式的关键是:分析项目中的变化部分与不变部分
  2. 策略模式的核心思想是:多用聚合/组合,少用继承,用行为类组合,而不是行为的继承,更加有弹性
  3. 体现了:对修改关闭,多扩展开发的原则,客户端增加行为不同修改原有的代码,只要添加一种策略即可,避免了使用多重转换语句
  4. 提供了可以替换继承关系的办法:策略模式将算法封装在独立的Strategy类中使得你可以独立于Context改变他,使他易于切换,理解,扩展
  5. 需要注意的是:每添加一个策略就要增加一个类,当策略过多时候会导致数目庞大
posted @ 2023-08-03 22:24  在赶路的我  阅读(76)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3