• 设计模式整理


    一、设计模式总述

    1. 什么是设计模式

       设计模式是一套经过反复使用的代码设计经验,目的是为了重用代码,让代码更容易被他人理解,保证代码可靠性。设计模式与己于人都是多赢的,它使得代码编写真正工程化,它是软件工程的基石,如同大厦的一块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现实种都有相应的原理来与之对应,每种模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是被广泛应用的原因。总体来说,设计模式分为三大类:

    • 创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式
    • 结构型模式(7种):适配器模式,装饰器模式,代理模式,桥接模式,外观模式,组合模式,享元模式
    • 行为型模式(11种):策略模式,模板方法模式,观察者模式,责任链模式。访问者模式,中介模式,迭代器模式,命令模式,状态模式,备忘录模式,解释器模式。

    2. 设计模式的六大原则

    (1)开闭原则
       开闭原则指的是对外扩展开放,对修改关闭。在对程序进行扩展的时候,不能去修改原有的代码,想要达到这样的效果,我们就需要使用接口或者抽象类
    (2)依赖倒置原则
       依赖倒置原则是开闭原则的基础,指的是针对接口编程,依赖抽象而不依赖于具体
    (3)里氏替换原则
      里氏替换原则是继承与复用的基石,只有当子类可以替换掉基类,且系统的功能不受影响时,基类才能被复用,而子类也能够在基础类上增加新的行为。所以里氏替换原则指的是任何基类可以出现的地方,子类一定可以出现
      里氏替换原则是对“开闭原则”的补充,实现“开闭原则”的关键步骤就是抽象化,而基类与子类的继承关系就是抽线化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。
    (4)接口隔离原则
      使用多个隔离的接口,比使用单个接口要好,降低接口之间的耦合度与依赖,方便升级与维护
    (5)迪米特原则
      也叫少知道原则,指的是一个类应当尽量减少与其他实体进行相互作用,使得系统宫嗯那个模块相对堵路,降低耦合关系。该原则的初衷是价格年底类的耦合,虽然可以避免与非直接的类通信,但是要通信,就必然会通过一个“中介”来发生关系,过分使用迪米特原则,会产生大量的中介和传递类,导致系统复杂度变大,所以采用迪米特法则要反复权衡,既要做到结构清晰,又要高内聚低耦合
    (6)合成复用原则
    尽量使用组合/聚合的方式,而不是使用继承

    二、java中的设计模式

    1. 创建型-工厂模式

    (1)简单工厂模式(编程习惯)

    定义一个工厂类,根据传入的参数的值不同返回不同的实例。被创建的实例具有共同的父类或接口(因为函数返回值需要相同,多态)。适用于
    1)需要创建的对象较少,
    2)客户端不关心对象的创建过程。
    优点:可以对创建的对象进行“加工”,对客户端隐藏相关细节;
    缺点:因创建逻辑复杂创建对象过多而造成代码臃肿,增加、删除某个子类均会违反开闭原则。

    我:对于不同品牌的产品,令该产品类继承一个通用产品类,利用工厂类的静态方法,通过输入型号即可获取对应品牌的产品实例。

    public interface Mouse {
    	void sayHi();  
    }
    
    public class DellMouse implements Mouse {
    	@Override
    	public void sayHi(){
    		System.out.println("我是dell鼠标");
    	}
    }
    
    public class HpMouse implements Mouse {
    	@Override
    	public void sayHi(){
    		System.out.println("我是hp鼠标");
    	}
    }
    
    public class MouseFactory{
    	public static Mouse creat(int type) {
    		switch(type) {
    			case 0: return new DellMouse();
    			case 1: return new HpMouse();
    			default: return new DellMouse();
    		}
    	}
    }
    
    public static void main (String[] args) {
    	Mouse mouse = MouseFactory.creatMouse(1);
    	mouse.sayHi(); 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    (2) 工厂方法模式

    定义一个用于创建对象的接口,让子类决定实例化哪一个类(不同的对象采用不同的工厂,dell鼠标,有dell工厂生产).对类的实例化延迟到其子类中 .优点:
    1)遵循开闭原则;
    2)对客户端隐藏对象创建细节;
    3)遵循单一职责。
    缺点:
    1)添加子类的时候“拖家带口”;
    2)只支持同一类产品的创建

    我:不同品牌的产品仍继承一个通用产品类不变,对于工厂类,为了避免新增一个品牌的产品就要去修改工厂类的代码(加一个产品创建的逻辑),所以将工厂类抽象出来作为一个工厂的接口,当要生产不同品牌的产品时,直接新建一个不同品牌的工厂去创建即可。

    public interface MouseFactory {
    	Mouse creatMouse();
    }
    
    public class HpMouseFactory implements MouseFactory {
    	@Override
    	public Mouse creatMouse(){
    		return new HpMouse();
    	}
    }
    
    public class DellMouseFactory implements MouseFactory {
    	@Override
    	public Mouse creatMouse(){
    		return new DellMouse();
    	}
    }
    
    public class FactoryMethodDemo{
    	public static void main(String[] args) {
    		MouseFactory mf = new HpMouseFactory();
    		Mouse mouse = mf.creatMouse();
    		mouse.syaHi(); 
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    (3) 抽象工厂模式

    抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,抽象工厂模式侧重同一产品族(dell的鼠标和键盘),工厂方法模式更加侧重于同一产品等级(dell的鼠标。没有键盘)。优点:
    1)解决了工厂模式只支持生产一种产品的弊端;
    3)新增一个产品族(品牌),只需要增加一个新的具体工厂,不需要改代码。
    缺点:
    1)添加新产品时,依旧违背开闭原则,增加系统复杂度。(Spring结合了工厂模式和反射机制的SpringIOC容器)

    我:在工厂模式的基础上,新增一个品牌对应的不同的产品,在工厂接口中增加创建不同产品的抽象方法,不同品牌的工厂子类去实现这些方法。

    public interface Keyboard {
    	void syaHello();
    }
    
    public interface Mouse {
    	void syaHi();
    }
    
    public class HpKeyboard implements Keyboard {
    	@Override
    	public void sayHello(){
    		System.out.println("我是惠普键盘 ");
    	}
    }
    
    public class DellKeyboard implements Keyboard {
    	@Override
    	public void sayHello(){
    		System.out.println("我是戴尔键盘 ");
    	}
    }
    public class HpMouse implements Mouse {
    	@Override
    	public void sayHi(){
    		System.out.println("我是惠普鼠标 ");
    	}
    }
    public class DellMouse implements Mouse {
    	@Override
    	public void sayHi(){
    		System.out.println("我是戴尔鼠标");
    	}
    }
    
    public interface Computerfactory {
    	Mouse creatMouse();
    	Keyboard creatKeyBoard(); 
    }
    
    public class DellComputerFactory implements ComputerFactory {
    	@Override
    	public Mouse createMouse() {
    		return new DellMouse();
    	}
    	
    	@Override
    	public Keyboard creatKeyBoard() {
    		return new DellKeyboard();
    	}
    }
    
    
    public class HpComputerFactory implements ComputerFactory {
    	@Override
    	public Mouse createMouse() {
    		return new HpMouse();
    	}
    	
    	@Override
    	public Keyboard creatKeyBoard() {
    		return new HpKeyboard();
    	}
    }
    
    public class AbstractFactoryDemo {
    	public static void main(String[] args) {
    		ComputerFactory cf = new HpComputerfactory();
    		Mouse mouse = cf.createMouse();
    		Keyboard ketboard = cf.createKeyboard();
    		mouse.syaHi();
    		Keyboard.say.hello();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    (4)工厂模式的小结对比

    • 工厂方法模式:每个抽象产品派生除多个具体产品类,每个抽象工厂类派生多个具体工厂类,每个具体工厂类负责一个产品的实体创建。
    • 抽象工厂模式:每个抽象产品派生多个具体产品类,每个抽象工厂派生多个具体工厂类,每个具体工厂负责一系列具体产品的实例创建。

    2. 创建型-建造者模式

    (1)什么是建造者模式?

    建造者模式将复杂产品的创建过程步骤分解在不同的方法中,使得创建过程更加清晰,从而更精确控制复杂对象的生产过程;通过隔离复杂对象的构建与使用,也就是将产品的创建与产品本身分离开来,使得同样的构建过程可以创建不同的对象并且每个建造者都相互独立因此可以很方便地替换具体建造者或则增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。

    (2)代码实现

      KFC里面一般都有好几种可供客户选择的套餐,它可以根据用户所点的套餐,然后再后面做这些套餐,返回给客户的是一个完整的套餐,下面我们将会模拟这个过程,我们约定套餐主要包含汉堡,薯条,可乐,鸡腿等组成部分,使用不同的组成部分就可以构建出不同的套餐。

    我:抽象出一个建造类的抽象类,对于要生成的不同对象新建不同的子类继承建造类的抽象类。最后控制者,会直接利用抽象类根据传入的不同子类实现创建不同的对象。

    • 套餐类,构建中执行的目标
    public class Meal {
        private String food;
        private String drink;
     
        public String getFood() {
            return food;
        }
     
        public void setFood(String food) {
            this.food = food;
        }
     
        public String getDrink() {
            return drink;
        }
     
        public void setDrink(String drink) {
            this.drink = drink;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 套餐构造器,最后执行者要操作这个构造器,(服务员操作这个就可以下单点餐)
    public abstract class MealBuilder {
        Meal meal = new Meal();
        
        public abstract void buildFood();
        
        public abstract void buildDrink();
        
        public Meal getMeal(){
            return meal;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 不同的套餐要继承套餐构造器(对于不同的套餐要有人去做,这个是实际去做的,后厨)
    public class MealA extends MealBuilder{
     
        public void buildDrink() {
            meal.setDrink("一杯可乐");
        }
     
        public void buildFood() {
            meal.setFood("一盒薯条");
        }
     
    }
    
    public class MealB extends MealBuilder{
     
        public void buildDrink() {
            meal.setDrink("一杯柠檬果汁");
        }
     
        public void buildFood() {
            meal.setFood("三个鸡翅");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 最后是KFC的服务员,它相当于一个指挥者,它决定了套餐是的实现过程,然后给你一个完美的套餐。(服务员拿着机器(通用构造器)进行点餐)
    public class KFCWaiter {
        private MealBuilder mealBuilder;
        
        public void setMealBuilder(MealBuilder mealBuilder) {
            this.mealBuilder = mealBuilder;
        }
     
        public Meal construct(){
            //准备食物
            mealBuilder.buildFood();
            //准备饮料
            mealBuilder.buildDrink();
            
            //准备完毕,返回一个完整的套餐给客户
            return mealBuilder.getMeal();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 测试类:(客人找一个服务员,选择一个套餐(做法),服务员将该套餐输入到点餐机器中(通用构造器)进行点餐,最后返回一个套餐)
    public class Client {
        public static void main(String[] args) {
            //服务员
            KFCWaiter waiter = new KFCWaiter();
            //套餐A
            MealA a = new MealA();
            //服务员准备套餐A
            waiter.setMealBuilder(a);
            //获得套餐
            Meal mealA = waiter.construct();
            
            System.out.print("套餐A的组成部分:");
            System.out.println(mealA.getFood()+"---"+mealA.getDrink());
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

    • 抽象工厂模式实现对产品族的创建,产品族指的是不同分类维度的产品组合,用抽象工厂模式不需要关心具体构建过程,只关心产品由什么工厂生产即可。而建造者模式则更关心的是对象的构建过程,要求按照指定的蓝图建造产品,主要目的是通过组装零配件而产生一个新产品。
    • 在抽象工厂模式中使用“工厂”来描述构建者,而在建造者模式中使用“车间”来描述构建者。

    3. 创建型-单例模式

      单例模式可以确保系统中某个类只有一个实例,该类自行实例化并向整个系统提供这个实例的公共访问点,除了该公共访问点,不能通过其他途径访问该实例。单例模式的优点在于:

    • 系统中只存在一个共用的实例对象,无需频繁创建和销毁对象,节约系统资源,提高系统性能
    • 可以严格控制客户怎么样以及何时访问单例对象。

    (1)懒汉单例模式

    Singleton通过私有化构造函数,避免类在外部被实例化,而且只能通过getInstance()方法获取Singleton的唯一实例。但是以上懒汉式单例的实现是线程不安全的,在并发环境下可能出现多个Singleton实例的问题

    public 	class Singlgton {
    	//懒汉模式,在第一次调用时候实例化自己
    	private Singleton(){};
    	private static Singleton single = null;
    	//静态工厂方法
    	public static Singleton geInstance(){
    		if (single == null) {
    			single = new Singleton();
    		}
    		return single;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    为了实现线程安全,采用单例双重检测的方式

    • 为什么getInstance() 方法内需要使用两个if(singleton == null) 进行判断呢?
      假设高并发下,线程A,B都通过了第一个if条件。若A先抢到了锁,new了一个对象,释放锁,然后线程B再强到锁,此时如果不做第二个if判断,B线程将会再new一个对象。使用两个if判断,确保了只有第一次调用单例的时候才会同步,这样线程安全的。
    • volatile关键字的作用?
      volatile的作用主要是禁止指定重排序。假设在不使用volatile的情况下,两个线程A,B都是第一次调用该单例方法,线程A先执行singleton = new Singleton(),但由于构造方法不是一个原子操作,编译后会生成多条字节码指令,由于java的指令重排,可能会先执行singleton的赋值操作,该操作实际只是在内存中开辟一片存储对象区域后直接返回内存的引用,之后singleton便不为空了,但是实际初始化操作还没有执行。如果此时线程B进入,就会拿到一个不为空的但是没有完成初始化的singleton对象,所以要加入volatile关键字,禁止指令重排优化。
    public class Singleton {
    	private Singleton(){};
    	private static volatile Singleton singleton = null;
    	public static Singleton getInstance() {
    		if (singleton == null) {
    			synchronized (Singleton.class) {
    				if (singleton == null) {
    					singleton = new Singleton();
    				}
    			}
    		}
    		return singleton;
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.创建型-原型模式

      原型模式也是用于对象的创建,通过将一个对象作为原型,对其进行复制克隆,产生一个与原对象类似的新对象。
      在java中,原型模式的核心就是原型类Prototype,Protype类需要具备以下两个条件:

    • 实现Cloneable接口:在java中Cloneable接口的作用就是在运行时通知虚拟机可以安全地在实现了Cloneable接口地类上使用clone()方法,只有实现了Cloneable的类才可以被拷贝,否则在运行时会抛出CloneNotSupportException异常
    • 重写Object类中的clone()方法,java中的所有类的父类都是Object,Object中有一个clone()方法用于返回对象的拷贝,但是其作用域protected,一般的类无法调用,因此,Prototype类需要将clone()方法的作用域改为public

      Object类中的clone()方法默认是浅拷贝,如果想要深拷贝对象,则需要clone()方法中定义自己的复制逻辑。

    • 浅复制:将一个对象复制后,基本数据类型的变量会重新创建,而引用类型指向的还是原对象所指向的内存地址
    • 深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建

      使用原型模式进行创建对象不仅简化对象的创建步骤,还比new方式创建对象的性能要好的多,因为Object类的clone()方法是一个本地方法,直接操作内存中的二进制流,特别是复制大对象时,性能非常查差。

    代码

    protected的可见范围是在该包范围内以及继承其的子类可见

    public class Cow implements Cloneable{
        private String name;
        private int age;
    
        public Cow(String name, int age) {
            this.name = name;
            this.age = age;
        }
        //无引用类型,直接clone即可
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone(); //直接抛出了,没用try-catch
        }
        @Override
        public String toString() {
            return "Cow{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    public class Sheep implements Cloneable{
        private String name;
        private int age;
        public Cow friend;//新朋友Cow对象,其余不变
        
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Object deep = null;
            //完成对基本数据类型(属性)和String的克隆
            deep = super.clone();
            //对引用类型的属性,进行再次clone
            Sheep sheep = (Sheep)deep;
            sheep.friend  = (Cow)friend.clone();
    
            return sheep;
        }
    
        public Sheep(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Sheep{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    public class Client {
        public static void main(String[] args) throws CloneNotSupportedException {
            Sheep sheepDolly=new Sheep("Dolly",2);
            sheepDolly.friend=new Cow("Tom",1); //并实例化朋友
    
            Sheep sheep1 = (Sheep)sheepDolly.clone();
            Sheep sheep2 = (Sheep)sheepDolly.clone();
            Sheep sheep3 = (Sheep)sheepDolly.clone();
            //....
    
            System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());
            System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');
    
            System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());
            System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');
    
            System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());
            System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');
            //...
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    在这里插入图片描述

    5.结构型-代理模式

    动态代理的内容https://blog.csdn.net/zpf336/article/details/82751925
    https://mingyang.blog.csdn.net/article/details/80981004?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-80981004-blog-106661697.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-80981004-blog-106661697.pc_relevant_paycolumn_v3&utm_relevant_index=2

    6。 结构型-动态代理

    [https://mingyang.blog.csdn.net/article/details/80981004?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-80981004-blog-106661697.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-80981004-blog-106661697.pc_relevant_paycolumn_v3&utm_relevant_index=2]

    代理模式的定义:由于某些原因需要给对象提供一个代理以控制该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介

    支付例子代码

    在日常生活中,我们进行手机支付,这个过程设计到从银行取钱,以及将钱打到收款方的账户,我们作为使用者,不需要这些冗余的操作,这些操作完全交给支付平台去搞定。

    • 抽象接口
    public interface Payment{
    	
    	public void pay(){};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 真实类
    public class RealPay implements Payment{
    	@Override
    	public void pay(){
    		System.out.println("作为用户,只关心支付,不关心过程");
    	}
    		
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 代理类
    public class AliPay implements Payment{
    	private Payment payment;
    	public AliPay(Payment payment) {
    		this.payment = payment;
    	}
    	private void beforePay() {
    		System.out.println("从银行取款");
    	}
    	@Override
    	public void pay() {
    		beforePay();
    		payment.pay();
    		afterPay();
    	}	
    	private void afterPay() {
    		System.out.println("将钱打给收款方");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    7. 结构型-外观模式(门面模式)

    子系统的外部与其内部的通信必须通过统一的对象进行。提供一个高层次的接口,使得子系统更易于使用。优点:

    • 为复杂的子系统提供一个简单的接口;
    • 对客户屏蔽子系统组件,因而减少了客户处理对象的数目,并使得子系统用起来更加方便。
    • 缺点:不符合开闭原则,即对外开放,对修改关闭(系统可以提供新的功能模块不必进行修改 )
      在这里插入图片描述
    public class LabourContractor {
    	 private Mason woker1 = new Mason();
    	 private BrickWorker woker2 = new BrikWorker();
    	 private BrikLayer woker3 = new BrikLayer();
    	 public void buildHouse(){
    	 	worker1.mix();
    	 	woker2.carry();
    	 	woker3.neat();
    	 }
    }
    
    public class Client{
    	LabourContractor labourContractor = new LabourContractor();
    	labourContractor.buildHouse();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    8. 行为型-模板方法模式

    围绕抽象类,实现通用逻辑,定义模板结构,部分逻辑由子类实现,其特点:

    • 复用代码:将相同部分的代码放在抽象的父类中进行复用而将具体的业务逻辑代码下沉到子类中
    • 反向控制:通过一个父类调用其子类的操作,通过对子类的具体实现扩展出不同的行为

    代码实现

    • 抽象类
    public abstract class KTYRoom {
    	public void procedure(){
    		openDevice();
    		orderSong();
    		orderExtra();
    		pay();
    	}
    	//模板自带方法,使用前必须得打开设备
    	private void openDevice() {
    		Systrm.out.println("打开视频和音响");
    	}
    	//子类必须实现的方法,必须得选歌
    	protected abstract void orderSong();
    	//钩子,额外开销视情况而定
    	protected void orderExtra();
    	//模板自带方法,用后必须付款
    	private void pay() {
    		Systrm.out.println("付款");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 子类(不同子类,有些操作是不同的)
    public ZhangSan extends KTYRoom {
    	@Override 
    	protected void orderSong(){
    		System.out.println("82年拉菲")
    	}
    	
    	@Override 
    	protected void orderExtra(){
    		System.out.println("来一份果盘");
    	}
    }
    
    
    public LiSi extends KTYRoom {
    	@Override 
    	protected void orderSong(){
    		System.out.println("82年雪碧");
    	}
    //	不要额外消费
    	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    9. 行为型-观察者模式

      观察者模式又称为发布-订阅模式,定义了对象之间的一对多依赖关系。当目标对象(被观察者)的状态发生变化时,他的所有依赖者(观察者)都会收到通知。一个观察目标可以对应多个观察者,而这些观察者之间没有相互联系,所以能够根据需求增加和删除观察者,使得系统易于扩展,符合开闭原则。并且观察者模式让目标对象和观察者松耦合,虽然彼此不清楚对方的细节,但是依然可以交互,目标对象只知道一个具体的观察者列表,但并不认识任何一个具体的观察者,它只知道他们都有一个共同的接口。
      但观察者模式的缺点在于如果很多个被观察者的话,那么嫁给你需要花费一定时间通知所有的观察者,如果观察者和被观察者之间存在循环依赖的话,那么可能导致系统崩溃,并且观察者模式没有相应的机制让观察者知道被观察者是怎么发生变化的,而仅仅只是知道观察目标发生变化。

    案例代码实现

    情景:在气象观测站中,他能够追踪目前的天气情况,包括温度,适度,气压。需要实现一个布告板,能够分别显示目前的状态,气象统计和简单的预报。当气象站中获取最新的测量数据时,三种布告板必须实时更新。

    • 主题接口(被观察者接口)
    public interface Subject {
    	//注册观察者
    	public void registerObserver(Observer observer);
    	//删除观察者
    	public void removeObserver(Observer observer);
    	//当主题状态发生改变时,这个方法需要被调用,以通知所有观察者
    	public void notifyObserver();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 观察者接口
    public interface Observer {
    	public void update(float temp, float humidity, float pressure);
    }
    
    • 1
    • 2
    • 3
    • 布告板显示接口
    public interface DisplayElment {
    	public void display();
    }
    
    • 1
    • 2
    • 3
    • WeatherData 实现主题接口 WeatherData(主题实例)
    public WeatherData implements Subject {
    	private List<Observer> observers;
    	private float tempterature;
    	private float pressure;
    	private float humidty;	
    
    	public WeatherData(){
    		observers = new ArrayList<Observer>();
    	}
    	
    	@Override 
    	public void notifyObserver() {
    		for (int i = 0; i < observers.size(); i++) {
    			Observer observer = observers.get(i);
    			observer.update(tempterature, pressure, humidty);
    		}
    	}
    	
    	@Override 
    	public void registerObserver(Observer observer) {
    		observers.add(observer);
    	}
    	
    	@Overried 
    	public void removeObserver(Observer observer) {
    		int i = observers.indexOf(observer);
    		if (i >= 0) {
    			observers.remove(i);
    		}
    	} 
    	
    	@Override 
    	public void measurementChanged(){
    		notifyObserver();
    	}
    	
    	public void setMeasurements(float temperature, float humidity, float pressure) {
    		this.temperature = temperature;
    		this.humidity = humidity;
    		this.pressure = pressure;
    		measurementChanged();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 布告板(同时作为观察者)
    public class CurrentConditionDisplay implements Observer, DisplayElement {
    	private float temperature;
    	private float humidity;
    	private Subject weatherData;
    	
    	public CurrentConditionDisplay(Subject weatherData) {
    		this.weatherData = weatherData;
    		weatherData.registerObserver(this);
    	}
    	
    	public void update(float temp, float hunidity, float pressure) {
    		this.temperature = temp;
    		this.humidity = humidity;
    		display();
    	}
    	
    	@Override
    	public void display() {
    		System.out.println("Current conditions:"+temperature+"F degrees and "+humidity+"% humidity");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 测试程序
    public class WeatherStation {
     
    	public static void main(String[] args) {
    		WeatherData weatherData = new WeatherData();//被观察者
    		
    		CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(weatherData);//观察者
    	
    		weatherData.setMeasurements(80, 65, 30.4f);//被观察者发布信息
    		weatherData.setMeasurements(82, 70, 29.2f);
    		weatherData.setMeasurements(78, 78, 40.4f);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    大数据知识点之大数据5V特征
    深入理解独占锁ReentrantLock类锁
    python爬虫之正则表达式学习
    上周热点回顾(2.6-2.12)
    2057. 值相等的最小索引
    使用 kubeconfig 文件组织集群访问
    C++之字符串语法
    代码大全2读书笔记【6-8章】
    基于WebApp的服装个性化定制订单管理系统
    CNN记录】pytorch中flatten函数
  • 原文地址:https://blog.csdn.net/weixin_42595206/article/details/124895828