设计模式的学习笔记

类与类之间的6种关系

在学习设计模式之前需要类与类之间的6种关系,分别是泛化,实现,依赖,关联,聚合,组合

泛化关系

泛化关系是依赖关系的一种特例,就是两类之间的继承,就是泛化关系

实现关系

实现关系是指实现接口,一个类实现一个接口,就叫做实现

依赖关系

只要两个类之间用到了对方,就叫做依赖关系,具体可以表现为:

  1. 一个类是另一个类的成员变量
  2. 一个类是另一个类中方法的返回值
  3. 一个类是另一个类中的成员变量
  4. 一个类是另一个类中方法的参数
  5. 一个类是另一个类中的局部变量

关联关系

关联关系是类与类之间的联系,具有单向关系和双向关系

聚合关系

聚合关系是指整体和部分的关系,整体和部分是可以相互分离的,是关联关系的一种特例

组合关系

组合关系是指整体和部分的关系,整体和部分是不可以相互分离的,也是关联关系的一种特例

设计模式的原则

在学习设计模式之前,所有的设计模式都需要遵守的设计模式的原则有6个,分别是:单一职责原则,接口隔离原则,依赖倒转原则,开闭原则,里氏替换原则,迪米特法则(最少知道原则)

单一职责原则

单一职责简单来说就是每一个类或者接口都只完成一个功能,这样不会再后面扩展时修改原始代码,只需要重新添加具体功能即可。

接口隔离了原则

当子类实现的接口中有很多的方法,但是在子类中又用不到这么多的方法,所以重写接口中的所有方法中会造成很多的冗余方法,因此需要将接口中的方法进行拆分,分成更小的接口

依赖倒转原则

依赖倒转原则的核心思想就是面向接口编程,依赖关系传递的三种方式分别为

  1. 接口传递
  2. 构造方法传递
  3. setter方法传递

开闭原则

很抽象,核心思想是对扩展开放对修改关闭

里氏替换原则

里氏替换原则是为了降低耦合度的,当类之间一旦有了继承关系,就会增加了耦合度,因此在想使用父类的方法,并且还需要扩展父类的功能时,可以不使用继承的方法,利用类与类之间的依赖关系,比如将原来的父类变成子类的成员变量,可以实现功能的扩展

迪米特原则

又叫做最少知道原则,是与直接的朋友通信,称出现在一个类中作为这个类的成员变量,方法参数或者方法的返回值的类,叫做朋友,而出现在局部变量中的类不算是朋友

23种设计模式

单例模式

就是整个项目中只能创建出一个实例化对象,因此要求构造器私有化,不能所有程序调用都可以实例化,还要提供外界的方法,用来调用实例化的唯一一个对象

饿汉模式的单例模式

故名思意,饿汉模式就是不管你用不用对象都创建出来对象,供你使用,分为两类

  1. 静态变量的饿汉模式,把实例化的对象修饰成变量的形式
package com.njupt.single;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/08/19:15
 * @Description:饿汉模式,不管你要不要,都创造出来等你用
 */
public class Hungry {
    
    
    public static void main(String[] args) {
    
    

        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();
        //结果为true说明是同一个对象。
        System.out.println(singleton1==singleton2);

    }
}
//采用静态变量的方式实现饿汉模式
/*
* 优点:在类加载是就完成了实例化,避免了线程同步问题
* 缺点:在类加载是创建出实例化对象,如果没有使用会造成内存的浪费
* */
class Singleton{
    
    
    private static Singleton singleton=new Singleton();
    private Singleton(){
    
    }

    public static Singleton getSingleton(){
    
    
        return singleton;
    }
}
  1. 写在静态代码块中的单例模式
package com.njupt.single;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/08/19:15
 * @Description:饿汉模式,不管你要不要,都创造出来等你用
 */
public class Hungry {
    
    
    public static void main(String[] args) {
    
    

        Singleton singleton1 = Singleton.getSingleton();
        Singleton singleton2 = Singleton.getSingleton();
        //结果为true说明是同一个对象。
        System.out.println(singleton1==singleton2);
    }
}
//采用静态变量的方式实现饿汉模式
/*
* 优点:在类加载是就完成了实例化,避免了线程同步问题
* 缺点:在类加载是创建出实例化对象,如果没有使用会造成内存的浪费
* */

class Singleton{
    
    
    private static Singleton singleton;
    private Singleton(){
    
    
    }
    static{
    
    singleton=new Singleton();}
    public static Singleton getSingleton(){
    
    
        return singleton;
    }
}

懒汉模式的单例模式

思想是什么时候调用方法,什么时候创建出来唯一的实例化对象给调用者使用

  1. 线程不安全的懒汉模式
package com.njupt.single;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/08/19:01
 * @Description:
 */
public class Lazy {
    
    
    public static void main(String[] args) {
    
    
        Single single = Single.getSingle();
        Single single1 = Single.getSingle();
        System.out.println(single==single1);
    }
}

/*优点:什么时候使用,什么时候才开始创建实例化对象
* 缺点:线程不安全的懒加载,当多个线程使用时,会造成线程安全问题
* */
class Single{
    
    
    private Single(){
    
    
    }
    private static Single single;

    public static Single getSingle(){
    
    
        if(single==null){
    
    
            single=new Single();
        }
        return single;
    }
}
  1. 最简单的线程安全的懒汉式,但是效率太低。
package com.njupt.single;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/08/19:01
 * @Description:
 */
public class Lazy {
    
    
    public static void main(String[] args) {
    
    
        Single single = Single.getSingle();
        Single single1 = Single.getSingle();
        System.out.println(single==single1);
    }
}

/*优点:什么时候使用,什么时候才开始创建实例化对象,并且是线程安全的
* 缺点:synchronized同步代码块锁的粒度太大,性能较差效率低
* */
class Single{
    
    
    private Single(){
    
    
    }
    private static Single single;

    public static synchronized Single getSingle(){
    
    
        if(single==null){
    
    
            single=new Single();
        }
        return single;
    }
}
  1. 双重校验锁
package com.njupt.single;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/08/19:01
 * @Description:
 */
public class Lazy {
    
    
    public static void main(String[] args) {
    
    
        Single single = Single.getSingle();
        Single single1 = Single.getSingle();
        System.out.println(single==single1);
    }
}

/*优点:什么时候使用,什么时候才开始创建实例化对象,并且是线程安全的
*推荐使用。。。
* */
class Single{
    
    
    private Single(){
    
    
    }
    //volatile关键字禁止指令重排,因为在创建对象时有三步
    /*
    * 1.分配内存
    * 2.初始化对象
    * 3将分配的内存指向初始化对象
    * 
    * 不加volatile会出现的问题
    * 如果两个线程同时进入了同步代码块,当第一个线程抢到锁然后结束之后,
    * 此时可能会先执行1.3两步,没有初始化对象,也就是此时single可能还为null,当第二个线程进入锁后
    * 判断single仍然为空,此时会在创建一个对象,就不满足单例模式的要求了,因此需要volatile关键字来禁止指令重排。
    *
    * */
    private static volatile Single single;
    public static  Single getSingle(){
    
    
        if(single==null){
    
    
            synchronized (Single.class){
    
    
                if(single==null){
    
    
                    single=new Single();
                }
            }
        }
        return single;
    }
}
  1. 静态内部类的懒加载
    利用了静态内部类的特点:
    1. 外部类加载的时候,其静态内部类不会立马被加载
    2. 在调用方法时,用到了静态内部类的变量之后,静态内部类才会被加载
package com.njupt.single;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/08/21:13
 * @Description:利用静态内部类来实现懒汉模式的单例
 */
public class StaticInnerClass {
    
    
    public static void main(String[] args) {
    
    

        SingleLi singe = SingleLi.getSinge();
        SingleLi singe2 = SingleLi.getSinge();
        System.out.println(singe==singe2);
    }
}
//静态内部类
class SingleLi{
    
    
    //构造方法
    private SingleLi(){
    
    }
    //静态内部类
    private static class InnerSingle{
    
    
        private static SingleLi singleLi=new SingleLi();
    }
    public static SingleLi getSinge(){
    
    
        return InnerSingle.singleLi;
    }
}

利用枚举实现单例模式

简单工厂模式

简单工厂模式是属于创建型模式,是工厂模式的一种,简单工厂模式是由一个工厂决定创建出哪一个产品类的实例,简单工厂模式是工厂模式家族中最简单使用的模式。
核心思想是定义一个创建对象的类,由这个类来封装实例化对象的行为。
在软件开发中,当我们会用到大量的创建某种或者某类或者某批对象时,就会使用到工厂模式。

package com.njupt.simpleFactory;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/09/17:24
 * @Description:简单工厂先出现一个抽象的父类,利用多态模式实现简单的工厂模式
 */

//先建立一个抽象的类,让其他的类来继承
public abstract class Operation {
    
    
    public abstract void doMath(int i,int j);
}
class Add extends Operation{
    
    

    @Override
    public void doMath(int i, int j) {
    
    
        System.out.println("相加之后的结果为:"+(i+j));
    }
}
class Sub extends Operation{
    
    

    @Override
    public void doMath(int i, int j) {
    
    
        System.out.println("相减之后的结果:"+(i-j));
    }
}
class Multiple extends Operation{
    
    

    @Override
    public void doMath(int i, int j) {
    
    
        System.out.println("两数相乘之后:"+(i*j));
    }
}
class Div extends Operation{
    
    

    @Override
    public void doMath(int i, int j) {
    
    
        System.out.println("两数相除之后:"+(i/j));
    }
}
//简单工厂,开始处理客户端传入的数据进行处理
class Factory{
    
    

    public static void startMath(int i,int j,String sym){
    
    
        Operation operation;
        if(sym.equals("+")){
    
    
            operation=new Add();
            operation.doMath(i,j);
        }else if(sym.equals("-")){
    
    
            operation=new Sub();
            operation.doMath(i,j);
        }else if(sym.equals("*")){
    
    
            operation=new Multiple();
            operation.doMath(i,j);
        }else if(sym.equals("/")){
    
    
            operation=new Div();
            operation.doMath(i,j);
        }else {
    
    
            System.out.println("输入的符号有误!");
        }
    }
}

客户端的测试代码

package com.njupt.simpleFactory;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/09/17:43
 * @Description:
 */
public class OperationTest {
    
    
    public static void main(String[] args) {
    
    
        Factory.startMath(3,4,"+");
        Factory.startMath(4,3,"-");
        Factory.startMath(3,4,"*");
        Factory.startMath(4,2,"/");
    }
}

结果:

相加之后的结果为:7
相减之后的结果:1
两数相乘之后:12
两数相除之后:2

Process finished with exit code 0 

工厂方法模式

抽象工厂模式

原型模式

原型模式是指:用原型实例创建出对象的种类,并且通过拷贝这些原型,创建新的对象,是一种创建型对象,简单的可以理解为复制出和原来对象一样的,即孙悟空拔出猴毛变成其他猴子一样。
工作原理是:通过一个原型对象传给那个要发动创建的对象,通过请求原型对象拷贝他们自己来创建一个一样的对象,即对象.clone().

好处:如果原始对象发生变化,其他克隆对象会发生相应的变化,无需修改代码。
缺点:需要为每一个类都写一个克隆方法,对原始类来说不难,但是要修改克隆之后的类时,需要修改其原始的类,就是要修改源码,违背了ocp原则。

一定要实现Cloneable接口才可以进行克隆

package com.njupt.prtoTypeMode;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/09/19:44
 * @Description:原型模式
 */
public class Monkey implements Cloneable{
    
    
    private String name;
    private String color;
    private String weapon;
    private int age;

    public Monkey() {
    
    
    }

    public Monkey(String name, String color, String weapon, int age) {
    
    
        super();
        this.name = name;
        this.color = color;
        this.weapon = weapon;
        this.age = age;
    }

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

    //原型模式要重写clone方法
    @Override
    protected Object clone()  {
    
    
        Monkey monkey=null;
        try {
    
    
          monkey= (Monkey) super.clone();
        } catch (Exception e) {
    
    
            System.out.println(e.getMessage());
        }
        return monkey;
    }
}

在spring框架中的xml配置bean标签的scope时,会使用到原型模式:prototype,此时就可以将实例化对象设置成原型模式,当有新的属性时,会和原来的类信息一样,但是不是一个类,因为不是单例模式。

原型设计模式的浅拷贝和深拷贝

  1. 浅拷贝
    浅拷贝:如果在进行clone过程中拷贝的是基本数据类型,就会进行值传递,如果拷贝的是引用数据类型,就会进行引用(内存地址)传递,就是将该成员的引用值(内存地址)复制一份给新的对象。例如,如果孙悟空的儿子属性,就会也是一个猴子属性,在进行原型模式进行克隆时,对于引用数据类型就是内存地址传递(引用传递)。
public class Monkey implements Cloneable{
    
    
    private String name;
    private String color;
    private String weapon;
    private int age;
    //当加入一个引用数据类型时:
    public static Monkey son;

测试结果:

public class ProtoTest {
    
    
    public static void main(String[] args) {
    
    
        Monkey monkey=new Monkey("孙悟空","黄色","金箍棒",25);
        monkey.son=new Monkey("孙小猴","黄色","小棍",2);
        Monkey monkey1 = (Monkey) monkey.clone();
        Monkey monkey2 = (Monkey) monkey.clone();
        Monkey monkey3 = (Monkey) monkey.clone();
        System.out.println(monkey+"引用数据类型的哈希值"+monkey.son.hashCode());
        System.out.println(monkey1+"引用数据类型的哈希值"+monkey1.son.hashCode());
        System.out.println(monkey2+"引用数据类型的哈希值"+monkey2.son.hashCode());
        System.out.println(monkey3+"引用数据类型的哈希值"+monkey3.son.hashCode());
    }
}

Monkey{
    
    name='孙悟空', color='黄色', weapon='金箍棒', age=25}引用数据类型的哈希值764977973
Monkey{
    
    name='孙悟空', color='黄色', weapon='金箍棒', age=25}引用数据类型的哈希值764977973
Monkey{
    
    name='孙悟空', color='黄色', weapon='金箍棒', age=25}引用数据类型的哈希值764977973
Monkey{
    
    name='孙悟空', color='黄色', weapon='金箍棒', age=25}引用数据类型的哈希值764977973

Process finished with exit code 0

结论:可以看出,在进行原型模式进行克隆时,对于基本数据类型直接赋值,而对于引用数据类型的数据,进行引用传递(内存地址)。

即浅拷贝默认使用clone()方法来实现的

2.深拷贝
深拷贝是:对于基本数据类型要复制值,对于引用数据类型,要申请存储空间,并复制每一个引用数据类型成员变量所引用的对象,对整个对象进行拷贝
深拷贝的实现方式
a. 重写clone方法来实现深拷贝

package com.njupt.prtoTypeMode;

import java.io.Serializable;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/09/20:48
 * @Description:孙悟空的引用数据类型的妻子
 */
public class Fox implements Cloneable, Serializable {
    
    
    private String name;
    private int age;

    public Fox() {
    
    
    }

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

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

    @Override
    //默认的拷贝方式是对基本数据类型进行值传递
    protected Object clone() throws CloneNotSupportedException {
    
    
        return super.clone();
    }
}

孙悟空的类信息

package com.njupt.prtoTypeMode;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/09/19:44
 * @Description:深克隆的演示
 */
public class Monkey implements Cloneable{
    
    
    private String name;
    private String color;
    private String weapon;
    private int age;
    //假设猴子有一个妻子
    public Fox wife;
    public Monkey son;

    public Monkey() {
    
    
    }

    public Monkey(String name, String color, String weapon, int age) {
    
    
        super();
        this.name = name;
        this.color = color;
        this.weapon = weapon;
        this.age = age;

    }

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

    //原型模式要重写clone方法
    @Override
    protected Object clone()  {
    
    
    //先对此类中的基本数据类型进行克隆,值传递
        Monkey monkey=null;

        try {
    
    
          monkey= (Monkey) super.clone();
          //对应用类型的属性进行单独处理
          monkey.wife=(Fox) wife.clone();
        } catch (Exception e) {
    
    
            System.out.println(e.getMessage());
        }
        return monkey;
    }
}

测试代码

package com.njupt.prtoTypeMode;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/09/20:52
 * @Description:
 */
public class DeepCloneTest {
    
    
    public static void main(String[] args) {
    
    
        Monkey monkey = new Monkey("monkey", "yellow", "bang", 25);
        monkey.wife=new Fox("狐狸",24);
        System.out.println(monkey+"深克隆之前的引用数据类型地址"+monkey.wife.hashCode());
        
        Monkey m2= (Monkey)monkey.clone();
        System.out.println(m2+"深克隆之后的引用数据类型地址"+m2.wife.hashCode());
    }
}

测试结果:发现引用数据类型wife的地址变了,说明进行了深克隆

Monkey{
    
    name='monkey', color='yellow', weapon='bang', age=25}深克隆之前的引用数据类型地址381259350
Monkey{
    
    name='monkey', color='yellow', weapon='bang', age=25}深克隆之后的引用数据类型地址824318946

Process finished with exit code 0

b.通过对象序列化来实现深拷贝
利用序列化的特点:可以实现引用数据类型的保存。
就可以先将原始类进行序列化,在进行反序列化,就可以实现引用数据类型的深拷贝

//将此方法加在monkey类中。
public Object deepClone(){
    
    
        //先创建流对象
        ByteArrayOutputStream bos=null;
        ObjectOutputStream oos=null;
        ByteArrayInputStream bis=null;
        ObjectInputStream ois=null;
        try {
    
    
            //序列化
            bos=new ByteArrayOutputStream();
            oos=new ObjectOutputStream(bos);

            //把当前对象以对象流的方式输出
            oos.writeObject(this);

            //反序列化
            bis=new ByteArrayInputStream(bos.toByteArray());
            ois=new ObjectInputStream(bis);
            Monkey monkey = (Monkey) ois.readObject();
            return monkey;
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return null;
        } finally {
    
    
            try {
    
    
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }

测试代码

public class DeepCloneTest {
    
    
    public static void main(String[] args) {
    
    
        Monkey monkey = new Monkey("monkey", "yellow", "bang", 25);
        monkey.wife=new Fox("狐狸",24);
        System.out.println(monkey+"深克隆之前的引用数据类型地址"+monkey.wife.hashCode());
        /*System.out.println(monkey+"深克隆之前的引用数据类型地址"+monkey.wife.hashCode());
        Monkey m2= (Monkey)monkey.clone();
        System.out.println(m2+"深克隆之后的引用数据类型地址"+m2.wife.hashCode());*/
        //第二种,直接进行序列化进行深克隆
        Monkey m2 = (Monkey) monkey.deepClone();
        System.out.println(m2+"深克隆之后的引用数据类型地址"+m2.wife.hashCode());
    }
}

结果:也实现了深克隆

Monkey{
    
    name='monkey', color='yellow', weapon='bang', age=25}深克隆之前的引用数据类型地址381259350
Monkey{
    
    name='monkey', color='yellow', weapon='bang', age=25}深克隆之后的引用数据类型地址693632176

Process finished with exit code 0

建造者模式

建造者模式又叫做生成器模式,是一种对象构建模式,它可以将复杂的对象的创建过程抽象出来。使这个抽象过程的不同实现方法可以构造出不同属性的对象。
建造者模式的4种角色
在这里插入图片描述

  1. Product(产品角色):一个具体的产品对象
  2. Builder(抽象创建者):创建一个Product对象的各个部件指定的接口/抽象类
  3. ConcreatBuilder(具体的创建者):实现抽象接口或者抽象类,负责构建和实现具体的零件功能
  4. Director(指挥者):构建一个Bulider接口的对象。它主要是用于创建一个复杂的对象,作用有两个,一是:隔离用户与对象的生产对象,二是负责控制产品对象的生产过程

以盖房子为例:
a.产品角色就是房子,房子视为对象,就是有不同的属性

public class House {
    
    
    private String base;
    private String wall;
    private String roof;}

b.抽象的创建者就是创建房子的各个步骤,该所有的房子都是要三步:打地基,砌墙,屋顶,变成抽象类,在写上抽象方法供建造不同款式的房子进行重写

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/10/12:03
 * @Description:建造者模式
 */
public abstract class BuildModel {
    
    
    //将与房子视为组合关系。
    House house=new House();
    abstract void buildBase();
    abstract void buildWall();
    abstract void buildRoof();
    public House build(){
    
    
        return house;
    }
}

c.具体的创建者:针对不同的房子类型,创建出不同的建造房子的方式,继承抽象方法,实现接口即可。
比如要建造一个普通的房子

package com.njupt.simpleFactory.buildModel;

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/10/12:50
 * @Description:
 */
public class NomalHouse extends BuildModel {
    
    
    @Override
    void buildBase() {
    
    
        System.out.println("建造普通方子的地基2米");
    }

    @Override
    void buildWall() {
    
    
        System.out.println("建造普通房子的墙3米");
    }

    @Override
    void buildRoof() {
    
    
        System.out.println("建造普通房子的屋顶小型");
    }
}

比如要建造一个别墅

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/10/13:46
 * @Description:
 */
public class VillaHouse extends BuildModel {
    
    
    @Override
    void buildBase() {
    
    
        System.out.println("别墅的地基3米");
    }

    @Override
    void buildWall() {
    
    
        System.out.println("别墅的墙5米");
    }

    @Override
    void buildRoof() {
    
    
        System.out.println("别墅的房顶大");

    }
}

d,.指挥者可以视为包工头,当客户找到包工头时,告诉他建造什么样的方法,他就会根据需求创建出房子

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/10/12:53
 * @Description:
 */
public class Boss {
    
    
//属性是针对整个抽象方法,这样可以更加灵活,关系是聚合
    private BuildModel buildModel;

    public Boss(BuildModel buildModel) {
    
    
        this.buildModel = buildModel;
    }

    public House creat(){
    
    
        //建造的具体过程也应该交给包工头去建造
        buildModel.buildBase();
        buildModel.buildWall();
        buildModel.buildRoof();
        //建造好之后就可以返回建造之后的结果
        return buildModel.build();
    }
}

最后,当客户前来建造房子时,只需要找到包工头,告诉他自己想要什么款式的房子,就可以。

/**
 * Creat with IntelliJ IDEA
 *
 * @Auther:倔强的加瓦
 * @Date:2021/10/10/12:53
 * @Description:
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        //通过构造器传入要盖多大的房子
        Boss boss = new Boss(new NomalHouse());
        //创建之后的结果返回给客户
        House creat = boss.creat();


        //再次建造一个别墅
        Boss boss1=new Boss(new VillaHouse());
        //创建好之后,将别墅返回给客户
        House villa = boss1.creat();
    }
}

注意事项:
创建者模式一般适合所创建的产品具有较多的共同点,组成部分相似,如盖房子都需要打地基,砌墙,封顶等步骤,就比较适合建造者模式,入伏哦产品之间的差异性很大,则不适合使用建造者模式
抽象工厂模式和建造者模式的区别:
抽象工厂模式实现对产品家族的创建,一个产品家族就是一系列的产品。具有不同费雷维度的产品组合

采用抽象工厂模式不需要知道构建的过程,只关心产品是由什么工厂生产的,而建造者模式则要求按照指定的蓝图生产产品,主要的目的是:组装零件生产一个新的产品。

适配器模式

作用是将原本不兼容的接口融合在一起工作,可以按照转换成兼容过程中的方式分成三类。

类适配器

使用场景:不能够使用原始类,需要借助一个适配器类来简介的使用,这个原始类叫做:source类,适配器类叫做Adapter类,转化之后的类叫做目标类dst
类适配器模式是指:
Adapter类通过继承src类,实现dst类接口,可以完成src–dst的适配。
类适配器的局限性:因为要使用原始的类,因此要继承原来的src类,但是因为java是单继承机制,所以dst类必须要用接口实现,也就是adapter类要通过继承src类,实现dst类接口,可以完成src–dst的适配。但是继承这一点是缺点,但是正是因为继承了原始的类,因此可以根据要求重写src类的方法,使得Adapter的灵活性增加了。

对象适配器

对象适配器思路和类适配器一样,知识将Adapter类做了修改,不是继承src类,而是将src类聚合到Adapter类中,实现dst接口,也可以完成src-dst的适配

解决了Adapter类适配器必须要继承src类的问题,用关联关系代替了继承关系,使成本更低,并且也不在要求dst必须是一个接口

接口适配器模式

使用场景:当不需要全部实现接口提供的默认方法时,可以使用接口适配器模式,就是先创建一个抽象的类,实现接口,然后对接口中的每一个方法提供一个默认的实现方法,然后抽象类可以有选择的覆盖父类的需要的方法进行重写。

桥接模式

桥接模式是指:将实现抽象放在两个不同的类层次中,是两个层次可以独立的改变结构,是一种结构型设计模式。主要的特点是:把抽象层与实现层分开,从而可以保持各部分的独立性以及应对他们功能的扩展。
桥接模式的原理图
在这里插入图片描述说明:
Client类是桥接模式的调用者
抽象类Abstraction:维护了Implementor(也包括他的实现类)两个是聚合关系,Abstraction是桥接类
Implementor是行为实现类的接口
接口类和抽象类是聚合的关系。
例如用桥接模式解决手机操作的问题:,如果希望添加一种直立样式的手机,按照桥接模式就比较方便
在这里插入图片描述首先将手机的品牌当作一个接口,里面抽象出所有品牌手机都应该有的功能,苹果华为等品牌实现接口

public interface Brand {
    
    
    public void music();
    public void play();
    public void video();
}

public class Apple implements Brand {
    
    
    @Override
    public void music() {
    
    
        System.out.println("苹果手机听音乐");
    }

    @Override
    public void play() {
    
    
        System.out.println("苹果手机玩游戏");
    }

    @Override
    public void video() {
    
    
        System.out.println("苹果手机看视频");
    }
}

public class Huawei implements Brand {
    
    
    @Override
    public void music() {
    
    
        System.out.println("华为手机听音乐");
    }

    @Override
    public void play() {
    
    
        System.out.println("华为手机玩游戏");
    }

    @Override
    public void video() {
    
    
        System.out.println("华为手机看视频");
    }
}

然后将手机类定义为抽象的类,里面聚合手机的品牌接口Brand,并且通过构造方法来进行传值,这样就可以针对不同的手机品牌实例化不同的品牌

public abstract class Phone {
    
    
    Brand brand;
    public Phone(Brand brand){
    
    
        this.brand=brand;
    }
    public void play(){
    
    
        brand.play();
    }
    public void video(){
    
    
        brand.video();
    }
    public void music(){
    
    
        brand.music();
    }
}

不同样式的手机继承抽象的类,重写相应手机在实现功能时自己的具体功能

//折叠手机的具体功能
public class FoldPhone extends Phone {
    
    
    public FoldPhone(Brand brand) {
    
    
        super(brand);
    }
    public void play(){
    
    
        System.out.println("折叠的");
        brand.play();
    }
    public void music(){
    
    
        System.out.println("折叠的");
        brand.music();
    }
    public void video(){
    
    
        System.out.println("折叠的");
        brand.video();
    }
}

触屏手机的具体功能

public class Touch extends Phone {
    
    
    public Touch(Brand brand) {
    
    
        super(brand);
    }
    public void play(){
    
    
        System.out.println("触屏的");
        brand.play();
    }
    public void music(){
    
    
        System.out.println("触屏的");
        brand.music();
    }
    public void video(){
    
    
        System.out.println("触屏的");
        brand.video();
    }
}

测试类顾客:

public class Client {
    
    
    public static void main(String[] args) {
    
    
        FoldPhone foldPhone = new FoldPhone(new Huawei());
        foldPhone.play();
        foldPhone.music();
        foldPhone.video();

        Touch touch = new Touch(new Huawei());
        touch.music();
        touch.play();
        touch.video();
    }
}

结果:

折叠的
华为手机玩游戏
折叠的
华为手机听音乐
折叠的
华为手机看视频
触屏的
华为手机听音乐
触屏的
华为手机玩游戏
触屏的
华为手机看视频

Process finished with exit code 0

当需要新添加直立样式的手机时,只需要重新继承抽象的手机类,然后写直立手机应该特有的功能即可。

装饰者模式

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

享元模式(FlyWeight Pattern)

共享对象
在java中,此模式可以解决重复对象的内存浪费问题,像需求相似度很高时,可以考虑使用享元模式来解决,如String字符串和线程池的创建都用到了享元模式
在这里插入图片描述图解说明:

  1. FlyWeight:是抽象的享元角色,他是产品的抽象类,同时定义出对象的外部状态(容易改变的状态,对象之间不可共享的信息)和内部状态(不轻易改变的状态,对象之间可共享的信息)的接口或实现类。
  2. ConcreteFlyWeight是具体的享元角色,是具体的产品类,实现抽象角色相关业务
  3. UnsharedConcreteFlyweight是不可共享的角色,一般不会出现在享元工厂中
  4. Factory是享元工厂类,用于构建一个池容器,同时还要提供从池中获取对象的方法。
    代码展示
//外部状态,针对不同的使用者来使用网站
public class User {
    
    
   private String name;
   public User(String name){
    
    
       this.name=name;
   }
}

//共享的抽象类
public abstract class FlyWeight {
    
    
    public abstract void use(User user);
}

//具体的实现类
public class ConcreteFlyWeight extends FlyWeight {
    
    
    private String type="";
    public ConcreteFlyWeight(String type){
    
    
        this.type=type;
    }

    @Override
    public void use(User user) {
    
    

        System.out.println(user+"当前正在使用"+type);
    }
}


//工厂类
public class flyWeightFactory {
    
    

    private static HashMap<String,FlyWeight> pool=new HashMap<>();
    public static FlyWeight getConcrete(String type){
    
    
        if(!pool.containsKey(type)){
    
    
            ConcreteFlyWeight weight = new ConcreteFlyWeight(type);
            pool.put(type,weight);
        }
        return pool.get(type);
    }
    public static int poolSize(){
    
    
        return pool.size();
    }
}

//测试类
public class WebTest {
    
    
    public static void main(String[] args) {
    
    
        FlyWeight concrete = flyWeightFactory.getConcrete("知乎");
        concrete.use(new User("小明"));
        FlyWeight weight = flyWeightFactory.getConcrete("斗鱼");
        weight.use(new User("zhangsan"));
        FlyWeight weight1 = flyWeightFactory.getConcrete("斗鱼");
        weight1.use(new User("历史"));
        FlyWeight weight2 = flyWeightFactory.getConcrete("斗鱼");
        weight2.use(new User("王维护"));
        System.out.println("线程池中的对象数:"+flyWeightFactory.poolSize());
    }
}


测试结果:可以看出虽然使用了4个对象,但是最后池中只有2个对象

User{
    
    name='小明'}当前正在使用知乎
User{
    
    name='zhangsan'}当前正在使用斗鱼
User{
    
    name='历史'}当前正在使用斗鱼
User{
    
    name='王维护'}当前正在使用斗鱼
线程池中的对象数:2

Process finished with exit code 0

猜你喜欢

转载自blog.csdn.net/m0_56184347/article/details/120656500
今日推荐