Java设计模式总结篇:七大原则以及23种设计模式简单介绍

java设计模式

  • 一、前言
  • 二、七大原则
    • 1、单一原则
    • 2、开闭原则
    • 3、里氏替换原则
    • 4、接口隔离原则
    • 5、依赖倒置原则
    • 6、迪米特法则
    • 7、合成复用原则
  • 三、23种设计模式
    • 1、单例模式(Singleton)
    • 2、工厂模式(Factoy)
    • 3、抽象工厂模式(AbstractFactory)
    • 4、原型模式(Prototype)
    • 5、建造者模式(Builder)
    • 6、适配器模式(Adapte)
    • 7、桥连模式(Bridge)了解
    • 8、装饰者模式(Decorator)
    • 9、享元模式(Flyweight)
    • 10、代理模式(Proxy)
    • 11、组合模式(Composite)
    • 12、外观模式(Facade)
    • 13、状态模式(State)
    • 14、责任链模式(Responsibility)
    • 15、迭代器模式(Iterator)
    • 16、观察者模式(Observer)重要
    • 17、策略模式(Strategy)
    • 18、模板方法模式(Temlapte Method)
    • 19、中介者模式(Mediator)
    • 20、备忘录模式(Memento)
    • 21、命令模式(Command)
    • 22、访问者模式(Visitor)
    • 23、解释器模式(Interpreter)

一、前言

Java设计模式在平时的开发中起到了至关重要的作用。设计模式的主要目的是:
1、降低耦合度,使得类的修改不至于“牵一发而动全身”。
2、提高可扩展性,新增功能对原有的代码没什么影响
3、提高可复用性,降低过多的使用类时,导致类爆炸的情况
4、提高灵活性,代码能够通过接口灵活调用
5、提高可维护性,修改的地方越少

二、七大原则

1、单一原则:一个类只负责一个职责
2、开闭原则:对修改关闭,对扩展开放
3、里氏替换原则:不要破坏继承关系
4、接口隔离原则:暴露最小接口,避免接口过于臃肿
5、依赖倒置原则:面向抽象编程
6、迪米特 法则:尽量不跟陌生人讲话
7、合成复用原则:多使用组合、聚合、少用继承

1、单一原则

一个方法 一个类只负责一个职责,各个职责的程序改动,不影响其它程序,实现 高内聚,低耦合

优点:降低耦合,提高可维护性,对一个类进行单独处理并且不影响其他类

2、开闭原则

对修改关闭,对扩展开放,核心是:抽象化,多态使用
优点:提高可扩展性,提高可维护性,提高可复用性

3、里氏替换原则

所有使用父类的地方,必须能够透明的使用子类对象

例如:父类Animal;子类Dog继承父类Animal,子类Cat继承父类Animal。

Animal animal=new Dog();
//同样可以透明的使用
Animal animal=new Cat();

当子类重写父类的方法时,则不适合使用里氏替换原则。

4、接口隔离原则

每一个接口承担独立的角色,只干自己该干的事情。
只暴露最小接口,实现类不需要用到实现的方法不要放在接口。

5、依赖倒置原则

指的是面向抽象编程,对抽象类或接口进行依赖,比较灵活。
比如:有抽象类Animal,子类Dog继承父类Animal,子类Cat继承父类Animal。在依赖的时候用抽象类,在使用的时候指定子类

//抽象类和子类
abstract class  Animal{
	abstract void hello();
}
class Dog extends Animal{
	@Override
	void hello() {
		System.out.println("hello ,I am dog");
	}
}
class Cat extends Animal{
	@Override
	void hello() {
		System.out.println("hello ,I am cat");	
	}
}	

//依赖到抽象类
class Client{
Animal a;
	public Client(Animal a){
		this.a=a;
	}
	public void hello(){
		a.hello();
	}
}
//测试类进行使用
public class Test {

    public static void main(String args[]) {
   //在使用到Animal的使用,需要定义它的子类
   	Client c=new Client(new Dog());
    c.hello();//打印指定的方法
    }
}

6、迪米特法则

尽量不要跟陌生人说话。
不是陌生人的情况:

  • 当前对象本身(this)
  • 方法参数
  • 对象的成员对象
  • 对象所创建的对象

目的:降低耦合,高内聚

7、合成复用原则

尽量使用组合、聚合的方式,少使用继承。
原因:继承会导致耦合度变高。
聚合方式的使用案例:

class A{}	
class Test{
A a;
	public void useA(A a){
		this.a=a;
	}
}

组合方式的使用案例:

class A{}	
class Test{
	A a=new A();
}

组合和聚合的区别 : 组合方式在使用Test对象的时候会立即开辟A对象的空间,而聚合的方式是在使用到A对象的时候才会开辟A对象的空间。

三、23种设计模式

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第1张图片
创建型模式:主要关注点是“怎样创建对象”

1、单例模式(Singleton)

特点:当需求是一个类只需要一个实例
单例模式有:1、饿汉模式;2、懒汉模式;3、双重检查模式;4、枚举模式

饿汉模式(推荐使用)

特点: 当类加载时创建好对象,并且在外部不能通过new创建该对象,只能调用这个类的的方法进行调用。线程安全,在实际开发中用的最多
案例:
方式一

/**
 * 饿汉方式一
 * @Author:小庄
 */
public class Singleton {
    //private不允许外部调用new创建对象
    private Singleton(){}
    //使用静态关键字”static“保证只有一次
    private static Singleton instance=new Singleton();
    //外部可直接通过类进行调用静态方法
    public static Singleton getInstance(){
        return instance;
    }
}
/**
 * 饿汉方式二
 * @Author:小庄
 */
class Singleton2 {
    private static Singleton2 instance;
    private Singleton2(){
        instance=new Singleton2();
    }
/**
 * 饿汉方式二
 * @Author:小庄
 */
class Singleton2 {
    private static Singleton2 instance;
    static {
        instance=new Singleton2();
    }
    private Singleton2(){}
    //外部可直接通过类进行调用静态方法
    public static Singleton2 getInstance(){
        return instance;
    }
}

懒汉模式(不推荐使用)

特点: 等需要该类的时候再加载,会考虑线程安全问题
1、不安全的懒汉模式

/**
 * 线程不安全的懒汉模式
 * @Author:小庄
 */
class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance(){
    	instance=new Singleton();
        return instance;
    }
}
class Singleton2{
    private static Singleton2 instance;
    private Singleton2(){}
    public static  Singleton2 getInstance(){
    	if(instance==null){
	    	synchronized(Singleton2.class){
	    		instance=new Singleton2();
	    	}	
    	}
        return instance;
    }
}

以上代码线程不安全的原因:当几个线程同时访问的时候,就有可能创建多个对象实例。

2、线程安全的懒汉模式

/**
 * 线程安全的懒汉模式
 * @Author:小庄
 */
class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static synchronized Singleton getInstance(){
    	if(instance==null){
    		instance=new Singleton();
    	}
        return instance;
    }
}

以上代码缺点:当getInstance()方法里面的逻辑代码很复杂时,所有的代码都被加锁,会大大的消耗性能

双重检查懒汉模式(线程安全)

/**
 * 线程安全的懒汉模式
 * @Author:小庄
 */
class Singleton{
    private static Singleton instance;
    private Singleton(){}
    public static  Singleton getInstance(){
    	if(instance==null){
    		//进行加锁
    		synchronized(Singleton.class){
    		//再进行一次判断类是否为空
				if(instance==null){
					instance=new Singleton();
				}
			}
    	}
        return instance;
    }
}

静态内部类实现单例

/**
 *内部类实现单例
 * @Author:小庄
 */
class Singleton{
    //设置为private
    private Singleton(){}
    //内部类的特性,外部类不能直接访问
    private static class GetSingleton{
       private static final Singleton instance=new Singleton();
    }
    public Singleton getInstance(){
        return GetSingleton.instance;
    }
}

运用枚举实现单例(推荐使用)

enum Singleton2{
    ONE,TWO,THREE;//一个属性代表一个实例,这里有三个实例
}

小结

推荐使用饿汉模式和枚举模式实现单例的原因是线程安全,而且简单明了
不推荐使用懒汉模式的原因是:1、部分的懒汉模式不安全 ;2、过于繁琐 ;3、有可能会对性能造成额外消耗

2、工厂模式(Factoy)

特点:对类的创建用一个工厂类进行管理。

工厂模式分为:简单工厂,工厂方法和抽象工厂,这节内容主要讲简单工厂和工厂方法。

简单工厂

由一个工厂对象决定创建出哪一种产品类的实例
代替构造函数创建对象,方法名比构造函数清晰。
简单工厂的类图:

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第2张图片
具体代码实现:

public class SimpleFactory{
	public createProduct(int i){
		switch (i){
            case 0: new Product1();break;
            case 1: new Product2();break;
            case 2: new Product3();break;
            default: System.out.println("没有该产品");
        }
	}
	//具体的使用者
	public static void main(String[] args){
		SimpleFactory factory=new SimpleFactory();
		//通过工厂进行创建类
		factory.createProduct(1);
	}
}
class Product1{}
class Product2{}
class Product3{}

工厂方法

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第3张图片

//抽象类
abstract class FactoryMethod {
		//生产方法
		public abstract void production(String productName);
}
class Product1 extends FactoryMethod {
	//实现抽象方法
	@Override
	public void production(String productName) {
		System.out.println("Product1生产了"+productName+"产品");
		
	}
}
class Product2 extends FactoryMethod {
	//实现抽象方法
	@Override
	public void production(String productName) {
		System.out.println("Product2生产了"+productName+"产品");
		
	}
}
//使用者
public class Client{
	public static void main(String[] args) {
		FactoryMethod factory=new Product1();
		factory.production("x");
	}
}

3、抽象工厂模式(AbstractFactory)

创建一组有关联的对象实例

角色:

  • AbstractProduct (抽象产品)
  • AbstractFactory (抽象工厂)
  • Client (委托者)

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第4张图片
具体实现如下,为了保证代码的阅读,这里不对类的具体操作进行展开

//定义抽象工厂类
abstract class AbstractFactory {
	//定义抽象方法,返回值是抽象产品类对象
	abstract AbstractClass1 createClass1();
	abstract AbstractClass2 createClass2();
	abstract AbstractClass3 createClass3();
}
//把抽象产品类全部定义完
abstract class AbstractClass1 {}
abstract class AbstractClass2 {}
abstract class AbstractClass3 {}
//定义具体产品类,分别继承对应的抽象产品类
class Class1 extends AbstractClass1{}
class Class2 extends AbstractClass2{}
class Class3 extends AbstractClass3{}
//定义具体工厂类,实现抽象方法,具体工厂类1
class Factory1 extends AbstractFactory{
	@Override
	AbstractClass1 createClass1() {
		// TODO Auto-generated method stub
		return new Class1();
	}
	@Override
	AbstractClass2 createClass2() {
		// TODO Auto-generated method stub
		return new Class2();
	}
	@Override
	AbstractClass3 createClass3() {
		// TODO Auto-generated method stub
		return new Class3();
	}
}
//具体工厂类2
class Factory2 extends AbstractFactory{
	@Override
	AbstractClass1 createClass1() {
		// TODO Auto-generated method stub
		return new Class1();
	}
	@Override
	AbstractClass2 createClass2() {
		// TODO Auto-generated method stub
		return new Class2();
	}
	@Override
	AbstractClass3 createClass3() {
		// TODO Auto-generated method stub
		return new Class3();
	}
}

分析

从上面代码中,我们可以发现,抽象工厂处理的是产品一族,它们和工厂方法的区别在于:工厂方法有利于处理一个产品的部件扩展维度,而抽象工厂有利于扩展产品一族维度。

4、原型模式(Prototype)

特点:必须实现Cloneable接口,并且重写clone方法,否则会报错
应用场景:一个对象属性特别多,同时指定很麻烦,实际工作中用的很少

角色:

  • Prototype(原型)
  • ConcretePrototype(具体原型)
  • Client(使用者)

浅克隆

//原型接口继承Cloneable接口,或者抽象类实现Cloneable接口
interface Prototype extends Cloneable{
}
//具体原型类实现原型接口,重写方法
public class ConcretePrototype implements Prototype {
    @Override
    protected Object clone() throws CloneNotSupportedException {
       //通过克隆自身对象
        ConcretePrototype p=(ConcretePrototype)this.clone();
        //返回克隆后的值
        return p;
    }
}

通过clone实现深拷贝

//原型接口继承Cloneable接口,或者抽象类实现Cloneable接口
interface Prototype extends Cloneable{
}
//具体原型类实现原型接口,重写方法
public class ConcretePrototype implements Prototype {
    //假设有一个对象属性,需要克隆这个对象属性
    public Book book;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //对基本属性进行克隆
        Object deep=null;
        deep=super.clone();
        //对引用类型的数据进行单独处理
        ConcretePrototype c=(ConcretePrototype )deep;
        c.book= (Book)book.clone();
        //返回克隆后的值
        return deep;
    }
}

缺点:繁琐
通过序列化实现深拷贝

public class Book implements Serializable{
  
    //浅克隆,使用默认的clone方法
    public Object deepClone() {

        ByteArrayInputStream bis=null;
        ByteArrayOutputStream bos=null;
        ObjectOutputStream oos=null;
        ObjectInputStream ois=null;
        try{
            bos=new ByteArrayOutputStream();
            oos=new ObjectOutputStream(bos);
            oos.writeObject(this);
            //反序列化
            bis=new ByteArrayInputStream(bos.toByteArray());
            ois=new ObjectInputStream(bis);
             Book book=(Book)ois.readObject();
            return book;
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5、建造者模式(Builder)

特点:处理复杂对象的创建,在使用时只需要用想要的属性时,不需要设置所有参数;这个建造者模式和模板方法特别像

角色:

  • Builder(建造者)
  • ConcreteBuilder(具体建造者)
  • Director(监工)
  • Client(使用者)
//建造者接口或抽象类
interface Builder {
	//具体步骤
	Builder creteBuilder();
	Builder creteBuilder2();
	Builder creteBuilder3();
}
//具体建造类
class ConcreteBuilder implements Builder{
	//实现具体步骤,返回值为当前对象,便于链式编程
	@Override
	public Builder creteBuilder() {
		System.out.println("建造第一层");
		return this;
	}
	@Override
	public Builder creteBuilder2() {
		System.out.println("建造第二层");
		return this;
	}
	@Override
	public Builder creteBuilder3() {
		System.out.println("建造第三层");
		return this;
	}
}
//监工
class Director {
	//将抽象类(接口)聚合,然后对抽象方法进行调用,会指向子类实现的方法
	Builder builder;
	public Director(Builder builder) {
		this.builder=builder;
	}
	public void toBuilder() {
		builder.creteBuilder().creteBuilder2().creteBuilder3();
	}
}
//使用者
public class Client {
	public static void main(String[] args) {
		//通过监工直接建造
		Director d=new Director(new ConcreteBuilder());
		d.toBuilder();
	}
}

比较常用的使用方式

//模拟建造者模式的具体使用
public class Person {
	//这里定义了很多的属性,但是有时候只需要部分的属性
	int id;
	String name;
	int age;
	double weight;
	int score;
	//将构造函数设置为私有属性,不让外部进行访问
	private Person() {}
	//定义内部类,通过内部类对类的成员变量进行操作,可以叫它为静态工厂
	public static class PersonBuilder{
		Person p=new Person();
		//每次方法返回值不为void,而是直接返回当前的对象,有利于链式方程
		public PersonBuilder basicInfo(int id,String name,int age) {
			p.id=id;
			p.name=name;
			p.age=age;
			return this;
		}
		public PersonBuilder weight(double weight) {
			p.weight=weight;
			return this;
		}
		public PersonBuilder score(int score) {
			p.score=score;
			return this;
		}
		//通过下面方法返回外部类对象
		public Person build() {return p;}
	}
	//打印
	public static void main(String[] args) {
		//采用链式编程,直观
		Person p=new Person.PersonBuilder().basicInfo(1, "张三", 18).weight(65.0).build();
	}
}

6、适配器模式(Adapte)

使不兼容的接口相融

角色:

  • Target(对象)
  • Adaptee(被适配)
  • Adapter(适配)

1、类适配器模式
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第5张图片

//目标接口
interface Target {
	public int open110V();
}
//被适配类
class ClassAdaptee{
	public int open220V(){
		return 200;
	}
}
//适配类
public class ClassAdapte extends ClassAdapteeimplements Target{
	@Override
	public int open110V() {
		int voltage=open220V();
		return voltage/2;
	}
}

2、对象适配器

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第6张图片
和类适配器的区别,适配器和被适配者之间没有继承关系,通过聚合实现(合成复用)

//目标类,可以用抽象类代替
abstract Target {
	public abstract int open110V();
}
//被适配类
class ClassAdaptee{
	public int open220V(){
		return 200;
	}
}
//适配类
public class ClassAdapte extends Target{
	public ClassAdaptee adaptee;
	public ClassAdapte(ClassAdaptee e){
		this.adaptee=e;
	}
	@Override
	public int open110V() {
		int voltage=e.open220V();
		return voltage/2;
	}
}

7、桥连模式(Bridge)了解

特点:
双维度扩展
分离抽象和具体
用聚合的方式连接抽象和具体

类图
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第7张图片

abstract class AbstractBridge {
	//聚合实现者
	Implementor imt;
}
//具体桥连者
class ConcreteBride extends AbstractBridge{
	//把实现者作为参数
	public ConcreteBride(Implementor imt) {
		this.imt=imt;
	}
}
//抽象化实现者
interface Implementor {
}
//具体实现者
public class ConcreteImplementor implements Implementor{
}

8、装饰者模式(Decorator)

特点:对类进行扩展时不修改原有的代码,为类添加新的功能,防止类继承带来的爆炸性增长

类图:
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第8张图片

案例应用:
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第9张图片

//定义Drink抽象类,并设定好抽象方法
 abstract class Drink {
	private String des;//描述
	private float price=0.0f;//价格
	public String getDes() {
		return des+"价格:"+price;
	}
	public void setDes(String des) {
		this.des = des;
	}
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	//计算费用的抽象方法
	protected abstract float cost();
}

//Coffee类继承Drink抽象类,因为Coffee只是一种饮料
abstract class Coffee extends Drink{
}
//定义具体咖啡,并设置好属性
class Cafe_Latte extends Coffee{

	public Cafe_Latte() {
		super.setDes("拿铁咖啡");
		super.setPrice(18.80f);
	}
	@Override
	protected float cost() {
		//获取价格
		return super.getPrice();
	}
}
//咖啡的具体实现类
class Instant_Coffee extends Coffee{
	
	public Instant_Coffee(int shuliang) {
		super.setDes("速溶咖啡");
		super.setPrice(14.00f);
	}
	@Override
	protected float cost() {
		return super.getPrice();
	}
}
class Cafe_Latte extends Coffee{

	public Cafe_Latte() {
		super.setDes("拿铁咖啡");
		super.setPrice(18.80f);
	}
	@Override
	protected float cost() {
		return super.getPrice();
	}
}
//通过继承Drink方便进行装饰类
 class DeCorator extends Drink{
	private Drink drink;
	private String des;//描述
	private float price=0.0f;//价格
	private int shuliang=1;//数量
	public DeCorator(Drink drink) {
		this.drink=drink;
	}
	public DeCorator(Drink drink,int shuliang) {
		this.drink=drink;
		this.shuliang=shuliang;
	}
	public String getDes() {
		return drink.getDes()+" "+" 加入"+shuliang+"份"+des+" "+des+"单价:"+price;
	}
	public void setDes(String des) {
		this.des = des;
	}
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	protected float cost() {
		float material_price=this.price*this.shuliang;
		return material_price+drink.cost();
	}
}
//装饰者的具体实现类
class Milk extends DeCorator{
	public Milk(Drink drink) {
		super(drink);
		super.setDes("牛奶");
		super.setPrice(2f);
	}
	public Milk(Drink drink, int shuliang) {
		super(drink, shuliang);
		super.setDes("牛奶");
		super.setPrice(2f);
	}
}
//装饰者的具体实现类
class Sugar extends DeCorator{
	public Sugar(Drink drink) {
		super(drink);
		super.setDes("糖");
		super.setPrice(1f);
	}
	public Sugar(Drink drink, int shuliang) {
		super(drink, shuliang);
		super.setDes("糖");
		super.setPrice(1f);
	}
}
//使用者测试类
public class Client {
	public static void main(String[] args) {
		Drink order=new Cafe_Latte();
		System.out.println("费用:"+order.cost());
		System.out.println(order.getDes());
		order=new Milk(order);
		System.out.println("加入一份牛奶的费用:"+order.cost());
		System.out.println(order.getDes());
	}
}

9、享元模式(Flyweight)

特点:共享数据,重复利用对象

应用场景:数据库连接池

类图:
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第10张图片
角色:

  • 抽象化享元对象
  • 享元对象
  • 享元池

下面是图书馆案例,图书馆作为享元池,书架作为抽象化享元对象,书籍作为享元对象。

来看类图
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第11张图片

//抽象化享元对象
abstract class Book {
	HashMap<String,Boolean> start=new HashMap<>();
	abstract void borrow(Lender lender);
	abstract void setBookStart(String bookName,boolean bookStart);
	abstract boolean getBookStart(String bookName);
}
//具体享元对象
class MixBook extends Book{
	private String bookName="";//书名
	private boolean bookStart;
	public MixBook(String bookName) {
		this.bookName=bookName;
	}
	@Override
	public void borrow(Lender lender) {
		System.out.println(lender.getName()+"向图书馆借出一本"+bookName);
	}
	public String getBookName() {
		return bookName;
	}
	public void setBookName(String bookName) {
		this.bookName = bookName;
	}
	public boolean getBookStart(String bookName) {
		if(start.get(bookName)) return true;
		else return false;
	}
	public void setBookStart(String bookName,boolean bookStart) {
		start.put(bookName, bookStart);
	}
}
//外部状态
class Lender {
	private String name;
	public Lender(String name) {
		this.name = name;
		
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
}
import java.util.HashMap;
//享元池
class Library {
	//设置图书馆为单例,只能生成一个实例对象
	private static Library library=new Library();
	//防止外部通过new生成实例
	private Library(){};
	//书架
	HashMap<String,Book> pool=new HashMap<>();
	
	public static Library getInstance() {
		return library;
	}
	public Book toBorrow(String bookName,Lender lender) {
		Book book=null;
		if(pool.isEmpty()) {
			System.out.println("图书馆目前没有书,请添加书籍");
		}
		else if(!pool.containsKey(bookName)) {
			//如果没有,提示图书馆没有这本书,请联系图书管理员
			System.out.println("图书馆没有"+bookName+",请联系图书管理员");
			
		}else {
			book=pool.get(bookName);
			if(!book.getBookStart(bookName)){
				System.out.println("你好"+lender.getName()+":"+bookName+"已经被借走了");
				book=null;
			}
			else {
				book.start.put(bookName, false);
				book.borrow(lender);
			}
		}
		return book;
	}
	public void shelveBook(String bookName,Book book) {
		book.setBookStart(bookName,true);
		pool.put(bookName, book);
	}
	public int poolSize() {
		return pool.size();
	}
}
//使用享元池
public class Student {
	public static void main(String[] args) {
		//通过单例模式进行创建图书馆享元池
		Library library=Library.getInstance();
		library.shelveBook("《Java编程思想》", new MixBook("《Java编程思想》"));
		library.shelveBook("《Mysql必会知识》", new MixBook("《Mysql必会知识》"));
		//设置借阅人
		Lender lender=new Lender("小庄");
		Book book1=library.toBorrow("《Java编程思想》",lender);
		Book book2=library.toBorrow("《Java编程思想》",lender);
		Book book3=library.toBorrow("《Mysql必会知识》",lender);
		//查看享元池借出的书籍
		System.out.println("图书馆借出书籍共:"+library.poolSize()+"个");
	}
}

10、代理模式(Proxy)

作用:在程序运行期间,在不修改源代码的情况下对方法进行功能增强

优势:减少重复代码,提高开发效率,并且便于维护

底层实现:在运行期间,通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

代理模式分为:静态代理,动态代理
本篇内容的动态代理只对jdk代理和cglib代理介绍

静态代理

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第12张图片
角色:

  • 代理类
  • 被代理类
  • 抽象化被代理类
  • 使用者
//抽象化被代理类
interface IBoard {
	void draw();
}
//具体被代理类
class Board implements IBoard{
	@Override
	public void draw() {
		System.out.println("被代理类画画操作执行");
	}
}
//静态代理类
class StaticProxy implements IBoard{
	//聚合抽象化被代理类
	private IBoard board;
	public StaticProxy(IBoard board) {
		this.board=board;
	}
	//通过代理类执行被代理类的方法
	@Override
	public void draw() {
		System.out.println("代理类执行自己操作");
		System.out.print("============");
		board.draw();//调用被代理类执行的方法
		System.out.print("============");
		System.out.println("代理类执行结束");
	}
}
//使用者
public class Client {
	public static void main(String[] args) {
		//通过静态代理类进行调用被代理类的方法
		new StaticProxy(new Board()).draw();
	}
}

动态代理模式之JDK代理

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第13张图片
jdk代理主要通过反射的方式进行代理,JDK代理本身就继承了Proxy类,由于Java不支持多继承,所以不支持对类的代理,只支持对接口的代理。
具体案例如下:

//接口1
interface IBoard {
	void draw();
}
//接口2
interface IComputer {
	void open();
	void complete();
}
//接口1的实现类
class Board implements IBoard{
	@Override
	public void draw() {
		System.out.println("被代理类画画操作执行");
	}
}
//接口2的实现类
class Computer implements IComputer{
	@Override
	public void open() {
		System.out.println("电脑正在启动....");	
	}
	@Override
	public void complete() {
		System.out.println("电脑启动完成");
	}
}
//代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

class JDKProxy {
	//设置被代理对象
	private Object target;
	//通过构造函数设置被代理对象
	public JDKProxy(Object target) {
	 this.target=target;
	}
	//被代理对象生成
	public Object getProxyInstance() {
		//返回一个JDK代理类对象
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {

			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				System.out.println("=================");
				System.out.println("代理开始");
				Object object=method.invoke(target, args);
				System.out.println("被代理的方法名:"+method.getName());
				if("open"==method.getName()) {
					Method complete=target.getClass().getMethod("complete");
					if(null!=complete){
						System.out.println("complete方法被代理对象自动调用");
						Thread.sleep(1000);
						complete.invoke(target, args);
					}
				}
				System.out.println("代理结束");
				System.out.println("=================");
				System.out.println();
				return object;
			}
		});
	}
}
//使用者
public class Client {
	public static void main(String[] args) {
		//代理画板对象
		JDKProxy board=new JDKProxy(new Board());
		IBoard iBoard=(IBoard)board.getProxyInstance();
		//调用画板的draw()方法
		iBoard.draw();
		//代理电脑对象
		JDKProxy computer=new JDKProxy(new Computer());
		IComputer iComputer=(IComputer) computer.getProxyInstance();
		//这里只调用了open()方法,但是complete会被代理对象自动调用
		iComputer.open();
	}
}

动态代理之cglib代理

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第14张图片
我们知道,使用Jdk代理的不足之处是不能对类进行代理,而Cglib代理刚好解决了这个问题。
Cglib可以对无接口的类进行代理,需要实现MethodInterceptor接口

具体案例:

//没有实现接口的类1
class Board{
	public void draw() {
		System.out.println("被代理类画画操作执行");
	}
}
//没有实现接口的类2
class Computer{
	public void open() {
		System.out.println("电脑正在启动....");
	}
	public void complete() {
		System.out.println("电脑启动完成");	
	}
}
//Cglib代理
import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//实现MethodInterceptor接口,重写intercept方法(实现代理功能)
class CglibProxy implements MethodInterceptor{
	//设置被代理类
	public Object target;
	//通过构造函数进行设置
	public CglibProxy(Object target) {
		this.target=target;
	}
	/**
	*实现代理的关键:
	*1.创建工具类Enhancer
	*2.设置它的父类,会在虚拟机生成一个父类
	*3.设置回调函数
	*4.创建子类对象,作为代理对象
	*/
	public Object getProxyInstance() {
		//创建工具类
		Enhancer e=new Enhancer();
		//设置父类
		e.setSuperclass(target.getClass());
		//设置回调函数
		e.setCallback(this);
		//创建子类对象,作为代理对象
		return e.create();
	}
	@Override
	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
		System.out.println("===============");
		//代理模式底层使用反射
		System.out.println("Cglib代理模式开始");
		//invoke激活,和jdk代理功能类似
		Object object=method.invoke(target, args);
		System.out.println("被代理的方法名:"+method.getName());
		//在代理类实现自己的功能
		if("open"==method.getName()) {
			Method complete=target.getClass().getMethod("complete");
			if(null!=complete){
				System.out.println("complete方法被代理对象自动调用");
				Thread.sleep(1000);
				complete.invoke(target, args);
			}
		}
		System.out.println("代理结束");
		System.out.println("=================");
		System.out.println();
		return object;
	}
}

另一种方式:不需要类去实现MethodInterceptor接口,是在回调函数的实现这个接口

//没有实现接口的类1
class Board{
	public void draw() {
		System.out.println("被代理类画画操作执行");
	}
}
//没有实现接口的类2
class Computer{
	public void open() {
		System.out.println("电脑正在启动....");
	}
	public void complete() {
		System.out.println("电脑启动完成");	
	}
}
//Cglib代理
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//实现MethodInterceptor接口,重写intercept方法(实现代理功能)
class CglibProxy2{
	//设置被代理类
	public Object target;
	//通过构造函数进行设置
	public CglibProxy2(Object target) {
		this.target=target;
	}
	/**
	*实现代理的关键:
	*1.创建工具类Enhancer
	*2.设置它的父类,会在虚拟机生成一个父类
	*3.设置回调函数
	*4.创建子类对象,作为代理对象
	*/
	public Object getProxyInstance() {
		//创建工具类
		Enhancer e=new Enhancer();
		//设置父类
		e.setSuperclass(target.getClass());
		//设置回调函数
		e.setCallback(new MethodInterceptor() {

			@Override
			public Object intercept(Object arg0, Method method, Object[] args, MethodProxy proxy) throws Throwable {
				System.out.println("===============");
				//代理模式底层使用反射
				System.out.println("Cglib代理模式开始");
				//invoke激活,和jdk代理功能类似
				Object object=method.invoke(target, args);
				System.out.println("被代理的方法名:"+method.getName());
				//在代理类实现自己的功能
				if("open"==method.getName()) {
					Method complete=target.getClass().getMethod("complete");
					if(null!=complete){
						System.out.println("complete方法被代理对象自动调用");
						Thread.sleep(1000);
						complete.invoke(target, args);
					}
				}
				System.out.println("代理结束");
				System.out.println("=================");
				System.out.println();
				return object;
			}
		});
		//创建子类对象,作为代理对象
		return e.create();
	}
}

11、组合模式(Composite)

一般适用于树状结构的场景,比如文件结构,比如层级结构
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第15张图片

实现代码如下:

//结点
abstract class Node {
	public abstract void printNode();
}
//导入相关的包
import java.util.ArrayList;
import java.util.List;
//根结点/子结点
class BranchNode extends Node{
	//给结点起个名字
	String nodeName;
	//将它以下结点进行收集
	public List<Node> nodes=new ArrayList<>();
	public BranchNode(String nodeName) {
		this.nodeName=nodeName;
	}
	public void addNode(Node node) {
		nodes.add(node);
	}
	//打印结点
	@Override
	public void printNode() {
		System.out.println(nodeName);
	}
}
//叶子结点
class LeafNode extends Node{
	//给结点起个名字
	String nodeName;
	public LeafNode(String nodeName) {
		this.nodeName=nodeName;
	}
	//打印叶子结点信息
	@Override
	public void printNode() {
		System.out.println(nodeName);
	}
}
//使用者
public class Client {
	public static void main(String[] args) {
		BranchNode root=new BranchNode("根结点");
		BranchNode node1=new BranchNode("结点1");
		Node c1=new LeafNode("叶子结点1");
		Node c2=new LeafNode("叶子结点2");
		//添加结点
		root.addNode(node1);
		//添加叶子结点
		node1.addNode(c1);
		node1.addNode(c2);
		//使用递归遍历	
		tree(root,0);
	}
	public static void tree(Node node,int depth) {
		for(int i=0;i<depth;i++) {
			System.out.print("--");
		}
		node.printNode();
		//判断是否是根结点
		if(node instanceof BranchNode) {
			//递归遍历结点
			for(Node n:((BranchNode) node).nodes){
				tree(n,depth+1);
			}
		}
	}
}

生活中用到组合模式的例子:学校,学院(系)和专业
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第16张图片

//结点
abstract class Composite {
	private String des;
	private String name;
	public Composite(String name,String des) {
		this.name=name;
		this.des=des;
	}
	protected void add(Composite composite) {
	}
	protected void remove(Composite composite) {}
	protected abstract void print();
	public String getDes() {
		return des;
	}
	public void setDes(String des) {
		this.des = des;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
//学院(系)
import java.util.HashSet;

class College extends Composite{
	HashSet<Major> majors=new HashSet<>();
	public College(String name,String des) {
		super(name,des);
	}
	@Override
	protected void add(Composite composite) {
		majors.add((Major)composite);
	}
	@Override
	protected void remove(Composite composite) {
		majors.remove((Major)composite);
	}
	@Override
	public String getName() {
		return super.getName();
	}
	@Override
	public String getDes() {
		return super.getDes();
	}
	@Override
	protected void print() {
		System.out.println("学院:"+getName());
		System.out.println();
		for(Major major:majors) {
		System.out.println("专业:"+major.getName()+"\t专业详情:"+major.getDes());
		}
		System.out.println("=====================");
	}
}
//导入相关包

import java.util.Set;
//学校
class University extends Composite{
	//学校由多个学院(系)组成
	Set<College> colleges=new HashSet<>();
	
	public University(String name,String des) {
		super(name,des);
	}
	@Override
	protected void add(Composite composite) {
		colleges.add((College)composite);
	}
	@Override
	protected void remove(Composite composite) {
		colleges.remove((College)composite);
	}
	@Override
	public String getName() {
		return super.getName();
	}
	@Override
	public String getDes() {
		return super.getDes();
	}
	@Override
	protected void print() {
		System.out.println("========"+getName()+"========");
		System.out.println("简介:"+getDes());
		System.out.println();
		for(College college:colleges) {
		System.out.println("学院:"+college.getName()+"\t学院详情:"+college.getDes());
		}
		System.out.println("=====================");
	}
}
//专业,对于叶子结点
public class Major extends Composite{
	public Major(String name,String des) {
		super(name,des);
	}
	@Override
	protected void print() {
		System.out.println(super.getName()+super.getDes());
	}
}
//使用者
public class Client {
public static void main(String[] args) {
	University university=new University("清华大学", "中国顶尖学府");
	College c=new College("计算机学院","培养软件+硬件高级人才");
	university.add(c);
	university.add(new College("金融学院", "培养金融行业高级人才"));
	university.print();
	c.add(new Major("计算机科学与技术","老牌的计算机专业"));
	c.print();
	}
}

12、外观模式(Facade)

外观模式又称为门面模式,通常和责任链模式结合使用
使用场景:解决多个类之间的复杂关系,只需要通过一个类就可以直接和其他类进行交互,具体使用:中间件

通过案例进行讲解:
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第17张图片

//经纪人,门面
class Agent {
	private static Agent agent=new Agent();
	//通过单例模式获取类对象
	protected Star star=Star.getInstance();;
	protected Fans fans=Fans.getInstance();
	protected Company company=Company.getInstance();
	private Agent() {}
	//见面机会方法
	protected void meeting() {
		System.out.println("粉丝"+fans.getName()+"与"+star.getName()+"获得一次见面机会");
	}
	//签约方法
	protected void contract() {
		System.out.println("明星"+star.getName()+"与"+company.getName()+"进行了签约");
	}
	public static Agent getInstance() {
		return agent;
	}
}
//明星类
class Star {
	private static Star star=new Star();
	protected volatile String name="";
	private Star() {}
	public String getName() {
		return name;
	}
	public synchronized void setName(String name) {
		this.name = name;
	}
	protected static Star getInstance() {
		return star;
	}
}
//粉丝
class Fans {
	private static Fans fans=new Fans();
	private volatile String name="";
	private Fans() {}
	public String getName() {
		return name;
	}

	public synchronized void setName(String name) {
		this.name = name;
	}
	public static Fans getInstance() {
		return fans;
	}
}
//签约公司
class Company {
	private volatile String  name="";
	private static Company company=new Company();
	private Company() {}
	public String getName() {
		return name;
	}
	public synchronized void setName(String name) {
		this.name = name;
	}
	public static Company getInstance() {
		return  company;
	}
}
//用户测试
public class Client {
	public static void main(String[] args) {
		//直接跟经纪人打交道
		Agent agent=Agent.getInstance();
		agent.company.setName("公司x");
		agent.fans.setName("张三");
		agent.star.setName("吴签");
		agent.meeting();
		agent.contract();
		System.out.println("============");
		agent.fans.setName("李四");
		agent.meeting();
		agent.contract();
	}
}

我们可以看到,经纪人就是一个门面,其他人都是通过经纪人来处理事情。粉丝想要见明星,首先要通过经纪人。再由经纪人和明星打交道。

13、状态模式(State)

如果一个类有很多个复杂状态的时候,可以把状态抽象出来,具体状态实现这个抽象类,

角色:

  • State(状态)
  • ConcreteState(具体状态)
  • Context(状况)
//设置抽象化状态类
abstract class State {
	public abstract void simle();
	public abstract void cry();
}
//开心状态
class HappyState extends State{
	@Override
	public void simle() {
		System.out.println("开心的笑");
	}
	@Override
	public void cry() {
		System.out.println("开心的哭了");
	}
}
//不开心状态
class SadState extends State{
	@Override
	public void simle() {
		System.out.println("沮丧的笑着");
	}
	@Override
	public void cry() {
		System.out.println("笑哭了");
	}
}
//状况
public class Context{
	//聚合状态
	private State state;
	//设置状态
	public Context(State state) {
		this.state=state;
	}
	//根据状态判断
	public void simle() {
		state.simle();
	}
	//根据状态判断
	public void cry() {
		state.cry();
	}
}

我们不难发现,抽象类的状态(抽象方法)是固定的,具体是怎样的状态,由子类实现。如果抽象类的状态(抽象方法)变化的很频繁,会对所有子类产生影响,那么就不建议使用这个模式

14、责任链模式(Responsibility)

是一种链式的结构的模式,通常使用在审批流,过滤器

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第18张图片

//设置责任链的抽象类(接口)
abstract class Approve {
	private Approve approve;
	//设置下一个审批人
	public void setApprove(Approve approve) {
		this.approve=approve;
	}
	//审批人的具体审批内容
	public abstract void toApprove();
	public abstract void chain(Approve approve);
}
//责任链的内容
//审批人1
class Approve1 extends Approve{
	//写入审批人的审批内容
	@Override
	public void toApprove() {
		System.out.println("我是审批人1,我审批完之后交给审批人2");
		//设置它的下一个审批人
		chain(new Approve2());
	}
	//设置它的下一个审批人
	@Override
	public void chain(Approve approve) {
		approve.toApprove();
	}
}
class Approve2 extends Approve {
	@Override
	public void toApprove() {
		System.out.println("我是审批人2,我审批完之后交给审批人3");
		chain(new Approve3());
	}
	@Override
	public void chain(Approve approve) {
		approve.toApprove();
	}
}
class Approve3 extends Approve {
	@Override
	public void toApprove() {
		System.out.println("我是审批人3,审批完成");
		
	}
	@Override
	public void chain(Approve approve) {
		approve.toApprove();
	}
}
//请求访问责任链
public class Request {
	public static void main(String[] args) {
		Approve approve=new Approve1();
		approve.toApprove();
	}
}

换一种思路,我们把每一个子类存到容器中,然后对容器进行遍历,然后执行方法,也类似这种链式的执行流程。
我们来通过代码来看看

abstract class Approve {
	//审批人的具体审批内容
	public abstract void toApprove();
}
//审批人1
class Approve1 extends Approve{
	//写入审批人的审批内容
	@Override
	public void toApprove() {
		System.out.println("我是审批人1,我审批完之后交给审批人2");
	}
}
//审批人2
class Approve2 extends Approve {
	@Override
	public void toApprove() {
		System.out.println("我是审批人2,我审批完之后交给审批人3");
	}
}
//审批人3
class Approve3 extends Approve {
	@Override
	public void toApprove() {
		System.out.println("我是审批人3,审批完成");
	}
}
//导入集合
import java.util.ArrayList;
//责任链管理类
class ApproveChain {
	//聚合了内容
	ArrayList<Approve> approves=new ArrayList<>();
	public ApproveChain addChain(Approve approve) {
		approves.add(approve);
		return this;
	}
	//通过调用这个方法,遍历容器中的类执行方法
	public void chain() {
		for(Approve a:approves) {
			a.toApprove();
		}
	}
}
//请求着使用
public class Request {
	public static void main(String[] args) {
		//定义责任链管理类
		ApproveChain ac=new ApproveChain();
		//需要设置它的下一个
		ac.addChain(new Approve1()).addChain(new Approve2()).addChain(new Approve3());
		ac.chain();
	}
}

上面代码很类似享元模式吧,哈哈。

15、迭代器模式(Iterator)

角色:

  • Iterator(迭代器)
  • ConcreteIterator(具体的迭代器)
  • Aggregate(集合)
  • CocreteAggreagete(具体集合)

类图:
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第19张图片
Iterator(迭代器)

public interface Iterator_ {
	boolean hashNext();
	Object next();
}

ConcreteIterator(具体的迭代器)

public class ArrayListIterator implements Iterator_{
	//单例模式创建类
	private static ArrayListAggregate alat=new ArrayListAggregate();
	@Override
	public boolean hashNext() {
		if(alat.currentIndex>=alat.size()) return true;
		return false;
	}
	@Override
	public Object next() {
		Object o=alat.list.get(alat.currentIndex);
		alat.currentIndex++;
		return o;
	}
}

Aggregate(集合接口)

public interface Aggregate {
	public abstract Iterator_ iterator();
}

CocreteAggreagete(具体集合)

import java.util.ArrayList;

public class ArrayListAggregate implements Aggregate{
	ArrayList<Object> list=new ArrayList<>();
	int index=0;
	int currentIndex=0;
	@Override
	public Iterator_ iterator() {
		// TODO Auto-generated method stub
		return new ArrayListIterator();
	}
	public int size() {
		
		return index;
	}
	public void add(Object o) {
		list.add(o);
		index++;
	}
}

16、观察者模式(Observer)重要

我们开发中,使用到观察者模式很多,这个知识点要重点把握

通知对象状态改变,允许一个对象向所有侦听对象广播自己的消息或事件

角色:

  • Source:事件源对象
  • Observer:观察者
  • Event:事件

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第20张图片

//事件,可定义为抽象化类(接口)
class Event {
	String loc;
	public Event(String loc) {
		this.loc=loc;
	}
}
//抽象化观察者
abstract class Observer {
	public abstract void actionEvent(Event event);
}
//具体观察者1
class Observer1 extends Observer{
	@Override
	public void actionEvent(Event event) {
		//打印
		System.out.println(event.loc);
		System.out.println("观察者1做出了行为");
		//执行相关的方法
	}
}
//具体观察者2
class Observer2 extends Observer{
	@Override
	public void actionEvent(Event event) {
		System.out.println("============");
		System.out.println("观察者2做出了行为");
		//执行相关的操作
	}
}

import java.util.ArrayList;
import java.util.List;
//由事件源发出事件,观察者进行处理事件
public class Source {
	private List<Observer> observers=new ArrayList<>();
	{	
		//注册观察者
		observers.add(new Observer1());
		observers.add(new Observer2());
	}
	//执行这个方法会触发所有观察者
	public void wakeUp() {
		for(Observer o:observers) {
			o.actionEvent(new Event("bed"));
		}
	}
}
//测试
class Test {
	public static void main(String[] args) {
		new Source().wakeUp();
	}
}

17、策略模式(Strategy)

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第21张图片
角色:

  • 抽象化策略
  • 具体策略
  • 聚合策略类
  • 使用者

封装的是不同的执行方式,比如对排序选择不同的排序算法(策略)
代码如下

//抽象化策略类(接口)
interface Sorter{
	public abstract void sort(Comparable[] data);
}
//具体策略,这里采用选择排序算法
class SelectSorter implements Sorter{
	@Override
	public void sort(Comparable[] data) {
		for(int i=0;i<data.length-1;i++) {
			int min=i;
			for(int j=i+1;j<data.length;j++) {
				if(data[min].compareTo(data[j])>0) {
					min=j;
				}
			}
			swap(data,i,min);
		}
	}
	public void swap(Comparable[] data,int i,int min) {
		Comparable temp=data[i];
		data[i]=data[min];
		data[min]=temp;
	}
}
//具体策略类,这里采用冒泡排序
class BubbleSorter implements Sorter{
	@Override
	public void sort(Comparable[] data) {
		//进行排序
		for(int i=1;i<data.length;i++) {
			for(int j=0;j<data.length-i;j++) {
				if(data[j].compareTo(data[j+1])>0) {
					Comparable temp=data[j+1];
					data[j+1]=data[j];
					data[j]=temp;
				}
			}
		}
	}
}
//聚合策略类,可以选择不同的策略
class SortAndPrint{
	Comparable[] data;
	Sorter sorter;
	public SortAndPrint(Comparable[] data,Sorter sorter) {
		this.data=data;
		this.sorter=sorter;
	}
	public void sort() {
		System.out.println("====排序前====");
		print();
		sorter.sort(data);
		System.out.println();
		System.out.println("====排序后====");
		print();
	}
	public void print() {
		for(int i=0;i<data.length;i++) {
			System.out.print(data[i]+",");
		}
		System.out.println(" ");
	}
}
//测试
public class Client {

	public static void main(String[] args) {
		//定义一个需要排序的数据
		String[] data= {"Dumpty","Bowman","Carroll"};
		SortAndPrint sap=new SortAndPrint(data, new BubbleSorter());
		sap.sort();
	}
}

18、模板方法模式(Temlapte Method)

具体实现交给子类
角色:

  • AbstractClass
  • concreteClass

类图:
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第22张图片

具体使用:

public abstract class AbstractDisplay {
	//定义抽象方法,由子类去实现
	public abstract void open();
	public abstract void print();
	public abstract void close();
	//对这个方法设定为final类型。表示子类不能重写
	public final void display() {
		open();
		print();
		close();
	}
	//使用
	public static void main(String[] args){
		CharDisplay chars=new CharDisplay();
		chars.display();
	}
}
//子类继承
class CharDisplay extends AbstractDisplay{
	@Override
	public void open() {
		System.out.println("打开CharDisplay");	
	}
	@Override
	public void print() {
		System.out.println("在CharDisplay进行打印");
	}
	@Override
	public void close() {
		System.out.println("关闭CharDisplay");
	}
}
//子类继承
class StringDisplay extends AbstractDisplay{

	@Override
	public void open() {
		System.out.println("打开StringDisplay");	
	}

	@Override
	public void print() {
		System.out.println("在StringDisplay进行打印");
	}

	@Override
	public void close() {
		System.out.println("关闭StringDisplay");
	}
 	public void printLine() {
		System.out.println("StringDisplay自定义的方法");
	}
}

应用场景:

  1. 算法的整体步骤很固定,但其中个别部分易变,将易变部分交给子类实现。
  2. 多个子类存在公共行为,可以将其提取出并集中到一个公共父类以避免代码重复
  3. 需要控制子类的扩展时,模板方法只在特定点调用钩子函数操作,这样就只允许在这些点进行扩展。

19、中介者模式(Mediator)

中介这个角色在生活中的理解是:比如租房者找中介,再由中介跟相关的房东沟通,房东也是通过中介和租房者沟通。中介是中间的桥梁。
在中介者模式中的理解和生活中的理解基本一致

角色:

  • Mediator 抽象化中介者
  • ConcentrateMediator 具体中介者
  • College 抽象化同事类
  • ConcentrateCollege 具体同事类(租房者、房东)

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第23张图片

//抽象化中介者
pabstract class Mediator {
	//沟通的方法
	public abstract void constact(String msg,Colleague c);
}
//具体中介类
class ConcreteMediator extends Mediator{
	//聚合同事类
	private ConcreteColleague1 c1;
	private ConcreteColleague2 c2;
	//设置get和set方法
	public ConcreteColleague1 getC1() {
		return c1;
	}
	public void setC1(ConcreteColleague1 c1) {
		this.c1 = c1;
	}
	public ConcreteColleague2 getC2() {
		return c2;
	}
	public void setC2(ConcreteColleague2 c2) {
		this.c2 = c2;
	}
	//具体的沟通方法,核心
	@Override
	public void constact(String msg, Colleague c) {
		//判断是哪个同事,进行处理事件
		if(c==c1) {
			c2.getMessage(msg);
		}else {
			c1.getMessage(msg);
		}
	}
}
//同事类
abstract class Colleague {
	//给同事类命名
	protected String name;
	//设置中介
	protected Mediator mediator;
	public Colleague(String name,Mediator mediator) {
		this.name=name;
		this.mediator=mediator;
	}
	//沟通的方法
	public abstract void constact(String msg);
	//获取信息
	public abstract void getMessage(String msg);
}
//具体的同事类,这个类表示租房
class ConcreteColleague1 extends Colleague{
	
	//在构造函数让该类被创建的时候同时命名
	public ConcreteColleague1(String name,Mediator mediator) {
		super(name, mediator);
	}
	@Override
	public void constact(String msg) {
		mediator.constact(msg, this);
	}
	@Override
	public void getMessage(String msg) {
		System.out.println();
		System.out.println("租房者姓名:"+name+"。收到的信息:"+msg);
	}
}
//具体的同事类,这里表示房东
class ConcreteColleague2 extends Colleague{

	//在构造函数让该类被创建的时候同时命名
	public ConcreteColleague2(String name,Mediator mediator) {
		super(name,mediator);
	}
	@Override
	public void constact(String msg) {
		mediator.constact(msg, this);
	}
	@Override
	public void getMessage(String msg) {
		System.out.println("房东姓名:"+name+"。收到的信息:"+msg);
	}
}
//测试类
public class Client {
	public static void main(String[] args) {
		//创建中介者
		ConcreteMediator cm=new ConcreteMediator();
		//创建同事类
		ConcreteColleague1 c1=new ConcreteColleague1("张三",cm);
		ConcreteColleague2 c2=new ConcreteColleague2("李四",cm);
		//房东需要知道具体的租房者和房东
		cm.setC1(c1);
		cm.setC2(c2);
		c1.constact("我想租两房一厅的房子");
		c2.constact("我这有,而且非常便宜");
	}
}

20、备忘录模式(Memento)

记录状态,便于回滚,经常和命令模式结合使用
Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第24张图片
角色:

  • Memento 备忘录,记录状态
  • Originator 创建者:创建备忘录
  • Caretaker 管理者:管理备忘录
//备忘录,需要存的内容
class Memento {
	private String state;
	public Memento(String state) {
		this.state=state;
	}
	public String getState() {
		return state;
	}
}
//创建者,创建备忘录
class Originator {
	private String state;
	public void setState(String state) {
		this.state=state;
		//打印状态
		System.out.println("现在的状态为:"+state);
	}
	public String getState() {
		return state;
	}
	//创建备忘录,并存储数据
	public Memento saveMemento() {
		return new Memento(state);
	}
	//获取备忘录的数据,恢复备忘录时的状态
	public void getStateFromMemento(Memento memento) {
		this.state=memento.getState();
	}
}
//管理者,备忘录的管理
import java.util.ArrayList;
class Caretaker {
	//聚合备忘录
	private ArrayList<Memento> mementos=new ArrayList<>();
	//添加备忘录
	public void addMemento(Memento memento) {
		mementos.add(memento);
	}
	public Memento getMemento(int index) {
		return mementos.get(index);
	}
}
//测试类
public class Client {
	public static void main(String[] args) {
		//定义创建类,管理类
		Originator o=new Originator();
		Caretaker c=new Caretaker();
		//设置当时的状态
		o.setState("状态1:攻击力:1000");
		c.addMemento(o.saveMemento());
		o.setState("状态2:攻击力:800");
		c.addMemento(o.saveMemento());
		String state=c.getMemento(0).getState();
		System.out.println("恢复后的状态为:"+state);
	}
}

备忘录模式能够随时恢复到备忘录保存的状态,而命令模式需要多个撤销恢复上一个状态。备忘录模也会用在存盘功能,将会使用到序列化和反序化,对于序列化和反序列化的相关使用可以查看这里:

序列化和反序列化的相关知识

可以尝试实现这个功能,这里就不展开介绍了。

21、命令模式(Command)

需要回退(撤销,Undo)状态的时候通常会使用到命令模式
多次撤销,需要结合责任链模式
宏命令,需要树状结构的组合模式结合使用

命令的调用者和命令的接收者进行了解耦

角色:

  • invok 调用者
  • Command 命令
  • Receive 接收者

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第25张图片
下面代码是对单个命令的执行和撤销作用

//抽象化命令接口
interface Command {
	void execute();
	void undo();
}
//具体命令
class Command1 implements Command{
	private Receive receive; 
	String str="https://blog.csdn.net/weixin_44715643?spm=1000.2115.3001.5343";
	public Command1(Receive receive) {
		this.receive=receive;
	}
	@Override
	public void execute() {
		System.out.println("接收者的内容:"+receive.msg);
		receive.msg+=str;
		System.out.println("接收者的内容发生了改变:"+receive.msg);
	}
	@Override
	public void undo() {
		//回退到原来的状态
		receive.msg=receive.msg.substring(0, receive.msg.length()-str.length());
		System.out.println("恢复回上一步的操作时的接收者的内容:"+receive.msg);
	}
}
//接收者,对命令做出相应的动作
class Receive {
	 String msg=new String("hello everybody ");
}
//调用者,聚合了命令
class Invok {
	private Command command;
	public Invok(Command command) {
		this.command=command;
	}
	public void executeCommand() {
		command.execute();
	}
	public void undoCommand() {
		command.undo();
	}
}
//使用命令者
public class Client {
	public static void main(String[] args) {
		Invok invok=new Invok(new Command1(new Receive()));
		//执行操作
		invok.executeCommand();
		//撤销操作
		System.out.println("下面进行撤销操作");
		invok.undoCommand();
	}
}

多个命令的执行和撤销

//抽象化命令接口
abstract class Command {
	abstract int getIndex();
	abstract void execute();
	abstract Command undo();
}
//命令1
import java.util.ArrayList;
class Command1 extends Command{
	private ArrayList<String> alist=new ArrayList<>();
	private int index=-1;
	private Receive receive; 
	//增加的内容
	String str="xxx ";
	public Command1(Receive receive) {
		this.receive=receive;
	}
	@Override
	public void execute() {	
		System.out.println("接收者的内容:"+receive.msg);
		receive.msg+=str;
		System.out.println("接收者的内容发生了改变:"+receive.msg);
		alist.add(receive.msg);
		index++;
	}
	@Override
	public Command1 undo() {
		if(index>=0) {
			if(index==0) {
				receive.msg=receive.msg.substring(0,receive.chushizhi.length());
				alist.remove(index);
				index--;
				System.out.println("初始值为:"+receive.msg);
			}else {
				//回退到上一步的状态
				alist.remove(index);
				//下标往前面移动一位
				String s=alist.get(--index);
				receive.msg=receive.msg.substring(0, s.length());
				System.out.println("恢复回上一步的操作时的接收者的内容:"+receive.msg);
			}
		}else {
			System.out.println("到底了,不能再撤销了");
		}
		return this;
	}
	//记录当前的状态
	@Override
	public int getIndex() {
		// TODO Auto-generated method stub
		return index;
	}
}
//接收者,对命令做出相应的动作
class Receive {
	 String msg="hello everybody ";
	//记录初始值
	String chushizhi=msg;
}
//调用者,可以作为使用者Client
class Invok {
	private Command command;
	public Invok(Command command) {
		this.command=command;
	}
	public void executeCommand() {
		command.execute();
	}
	public Invok undoCommand() {
		command.undo();
		return this;
	}
}
//使用命令者
public class Client {
	public static void main(String[] args) {
		Invok invok=new Invok(new Command1(new Receive()));
		//执行操作
		invok.executeCommand();
		invok.executeCommand();
		invok.executeCommand();
		//撤销操作
		System.out.println("下面进行撤销操作");
		invok.undoCommand();
		invok.undoCommand();
		invok.undoCommand();
		invok.undoCommand();
	}
}

22、访问者模式(Visitor)

数据结构和数据分离

角色:

  • Visitor 访问者抽象类 - Element 具体元素
  • Struct 结构,由多个元素组成
  • 具体元素

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第26张图片

//抽象化接口
interface Visitor {
	void visitCpu(Cpu cpu);
	void visitMemory(Memory memory);
	void visitBoard(Board board);
}
//具体访问者
class Woman implements Visitor{
	//全程七折半
	double totalPrice=0.0;
	@Override
	public void visitCpu(Cpu cpu) {
		//打折活动
		totalPrice+=cpu.getPrice()*0.75;
	}
	@Override
	public void visitMemory(Memory memory) {
		//打折活动
		totalPrice+=memory.getPrice()*0.75;
	}

	@Override
	public void visitBoard(Board board) {
		//打折活动
		totalPrice+=board.getPrice()*0.75;
	}
	//获取价钱
	public double getPrice() {
		return totalPrice;
	}
}
//具体访问者
class Man implements Visitor{
	//购买需要花费的总价钱,全场八折!
	double totalPrice=0.0;
	@Override
	public void visitCpu(Cpu cpu) {
		//打折活动
		totalPrice+=cpu.getPrice()*0.8;
	}
	@Override
	public void visitMemory(Memory memory) {
		totalPrice+=memory.getPrice()*0.8;
	}
	@Override
	public void visitBoard(Board board) {
		totalPrice+=board.getPrice()*0.8;
	}
	//获取价钱
	public double getPrice() {
		return totalPrice;
	}
}
//结构
interface ComputerPart {
	void accept(Visitor visitor);
}
class Board implements ComputerPart{
	//接待指定的访问人,调用指定的方法
	@Override
	public void accept(Visitor visitor) {
		visitor.visitBoard(this);
	}
	//设置Board的单价
	public double getPrice() {
		return 1000.00;
	}
}
class Cpu implements ComputerPart{
	//接待指定的访问人,调用指定的方法
	@Override
	public void accept(Visitor visitor) {
		visitor.visitCpu(this);
	}
	//设置Cpu的单价
	public double getPrice() {
		return 800.0;
	}
}
class Memory implements ComputerPart{
	//接待指定的访问人,调用指定的方法
	@Override
	public void accept(Visitor visitor) {
		visitor.visitMemory(this);
	}
	//设置Memory的单价
	public double getPrice() {
		return 400.00;
	}
}
//结构类,根据不同的访问者设定标配的电脑配件
public class Computer {
	Cpu cpu=new Cpu();
	Memory memory=new Memory();
	Board board=new Board();
	//给访问者设置标配
	public void accept(Visitor visitor) {
		this.cpu.accept(visitor);
		this.memory.accept(visitor);
		this.board.accept(visitor);
	}
}
//测试类
public class Test {
	public static void main(String[] args) {
		//定义相关类
		Computer computer=new Computer();
		Man man=new Man();
		Woman woman=new Woman();
		//通过访问者模式进行使用
		//一个男人过来购买电脑
		computer.accept(man);
		System.out.println("男人过来买需要花费:"+man.getPrice());
		//一个女人过来购买电脑
		computer.accept(woman);
		System.out.println("女人过来买需要花费:"+woman.getPrice());		
	}
}

23、解释器模式(Interpreter)

对于一些固定文法构建一个解释句子的解释器。在实际开发中,我们很少会去开发一个解释器,因为涉及到的知识点比较复杂。所以我们只需要了解即可,我们通过加减法的例子去了解它

角色:

  • Expression 抽象化解释器
  • Context 上下文,用于存放变量和值
  • Addition 加法解释器(具体解释器)
  • Minus 减法解释器(具体解释器)
  • Variable 变量解释器 (具体解释器),它主要和上下文打交道(获取变量指定的值)

Java设计模式总结篇:七大原则以及23种设计模式简单介绍_第27张图片

//抽象化解释器
abstract class Expression {
	//解释方法
	public abstract int interpret(Context context);
}
//导入相关的包
import java.util.HashMap;
//上下文
class Context {
	//定义一个键值对的容器
	private HashMap<Variable,Integer> map=new HashMap<>();
	//把变量名和值存放到容器中
	public void addVariable(Variable v,Integer x) {
		map.put(v, x);
	}
	//通过变量名获取值
	public int getVlaue(Variable v) {
		return map.get(v);
	}
}
//变量类,继承解释器
class Variable extends Expression{
	//给每个变量定义一个名字
	String name;
	//通过构造方法给变量名赋值
	public Variable(String name) {
		this.name=name;
	}
	//变量解释方法,通过变量获取值
	@Override
	public int interpret(Context context) {
		//通过变量名获取上下文的容器中对应的值
		return context.getVlaue(this);
	}
	//重写toString()方法
	@Override
	public String toString() {
		return name;
	}
}
//具体解释器,加法器
class Addition extends Expression{
	//定义两个解释器的类,表示传进来的变量
	private Expression left;
	private Expression right;
	//存放进去的是变量
	public Addition(Expression left,Expression right) {
		this.left=left;
		this.right=right;
	}
	//进行加法运算
	@Override
	public int interpret(Context context) {
		//通过变量的解释器,获取对于的值,进行运算
		return left.interpret(context)+right.interpret(context);
	}
	@Override
	public String toString() {
		//重写toString方法
		return "("+left.toString()+"+"+right.toString()+")";
	}
}
//具体解释器,模拟减法器
class Minus extends Expression{
	//定义两个解释器变量
	private Expression left;
	private Expression right;
	//通过构造函数的方式传入两个变量
	public Minus(Expression left,Expression right) {
		this.left=left;
		this.right=right;
	}
	//解释方法
	@Override
	public int interpret(Context context) {
		//通过变量的解释器,获取对于的值,进行运算
		return left.interpret(context)-right.interpret(context);
	}
	@Override
	public String toString() {
		//重写toString()方法
		return "("+left.toString()+"-"+right.toString()+")";
	}
}
//测试类
public class Client {
	public static void main(String[] args) {
		//创建上下文
		Context context=new Context();
		//创建变量
		Variable a=new Variable("a");
		Variable b=new Variable("b");
		Variable c=new Variable("c");
		Variable d=new Variable("d");
		//将变量赋予值,并保存到上下文
		context.addVariable(a, 1);
		context.addVariable(b, 2);
		context.addVariable(c, 3);
		context.addVariable(d, 4);
		//设置表达式
		Expression expression=new Addition(a,new Minus(b,new Addition(c,d)));
		//对表达式进行解释
		int result=expression.interpret(context);
		//打印输出结果
		System.out.println(expression.toString()+"="+result);
	}
}

结语

通过上面的学习,我们发现很多设计模式都十分相似,其实这很正常,因为这些设计模式都是基于Java的继承、多态、聚合、组合等方式完成的。我们应该学习这些模式的设计思想,它们适合在什么场景,并且我们不需要归根到底是哪个设计模式,只要我们类的设计的合理,达到我们的需求即可。通过学习这个知识点,我们能够更加深入的理解开发框架的底层设计原理,有助于我们后面的学习。
我是小庄,欢迎大家和我一起成长。

你可能感兴趣的:(笔记,java,设计模式)