浅析23种设计模式

设计模式

日期:2021年2月6日;

作者:木子六日;

细说一下23种设计模式;

按照四人帮的分法可以分为以下3类:创建型模式、结构型模式、行为型模式;

创建型模式

这类设计模式提供了我们创建对象的不同方法。

单例模式

有些对象我们只希望存在一个,这时候就用到了单例。

简单来说可以分为饿汉式懒汉式两种,两种方式都是通过私有化构造器达到单例的效果。

饿汉式的版本:

package singleton;

public class Singleton1 {
	private static final Singleton1 instance = new Singleton1();

	private Singleton1() {
	}

	public static Singleton1 getInstance() {
		return instance;
	}
	
    /**
 	 * 饿汉式:在类加载的之后直接实例化,保证线程安全;
     * 缺点:你不用他实例化他干嘛呀;
     * 此写法简单实用;
     */
	public static void main(String[] args) {
		Singleton1 singleton1 = Singleton1.getInstance();
		Singleton1 singleton2 = Singleton1.getInstance();
		System.out.println(singleton1 == singleton2);
	}
}

这可以说是最常用的单例写法,写起来也简单,也保证了线程安全;

懒汉式:

package singleton;
/**
 * 但是如果锁住整个方法的话效率过低;
 * 可使用同步代码块提高效率;
 * @author 木子六日
 *
 */
public class Singleton4 {
	private static volatile Singleton4 instance;
	
	private Singleton4() {
	}
	
	public static Singleton4 getInstance() {
		if (instance == null) {
			// 这里为什么要双重检查呢?
			// 可能有两个线程都走到了这里,线程A进入了同步方法。
			// 线程B在等待锁的释放,A执行完,new了一个对象释放了锁。
			// 如果同步块中不进行判断,那么B也会new一个对象出来。
			synchronized (Singleton4.class) {
				if (instance == null) {
					instance = new Singleton4();
				}
			}
		}
		return instance;
	}
}

下面是一种完美写法,是饿汉式的改进:

package singleton;
/**
 * 完美写法
 * 利用静态内部类;
 * 外部类加载时,静态内部类不会加载,实现了饿汉式写法的懒加载
 */
public class Singleton5 {
	private Singleton5() {
	}
	
	private static class Singleton5Holder {
		private static final Singleton5 instance = new Singleton5();
	}
	
	public static Singleton5 getInstance() {
		return Singleton5Holder.instance;
	}
}

原型模式

就是把一个对象一模一样复制一遍,java自身提供了Cloneable接口以及Object的clone方法(native方法)实现了原型模式。克隆又分为深克隆浅克隆。深克隆是复制对象的所有内容,浅克隆只复制基本数据类型,对象仍是复制的引用。

Job类:

package prototype;

public class Job implements Cloneable{
	private String jobName;
	public Job(String jobName) {
		this.jobName = jobName;
	}
	public String getJob() {
		return this.jobName;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

Person类聚合了Job:

package prototype;

public class Person implements Cloneable{
	private String name;
	private int age;
	private Job job;
	public Person(String name, int age, Job job) {
		super();
		this.name = name;
		this.age = age;
		this.job = job;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Job getJob() {
		return job;
	}
	public void setJob(Job job) {
		this.job = job;
	}
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Person p = (Person) super.clone();
		p.job = (Job) job.clone();
		return p;
	}
}

客户端调用:

package prototype;

public class Main {
	/**
	 * 原型模式:对于已有的一个类直接进行复制操作;
	 * java已经实现了原型模式:
	 * 如果你想克隆一个对象,此类必须实现Cloneable接口,重写clone方法;
	 * 但是如果类属性中存在其他对象,调用clone方法时仅仅是浅克隆;
	 * 即复制出来的对象的此属性还是指向原对象的那个属性对象;
	 * 道理很简单,clone方法只是把内存复制一遍,存的还是引用;
	 * 如果需要深克隆,那么此属性也要实现克隆,以此类推;
	 * 同时我们在重写clone方法时也要重新执行此属性的克隆操作,而不仅仅是返回Object的克隆结果;
	 */
	public static void main(String[] args) throws CloneNotSupportedException {
		//以下为一个深克隆的例子
		Person p1 = new Person("ljj", 18, new Job("programmer"));
		Person p2 = (Person) p1.clone();
		System.out.println(p1.getJob() == p2.getJob());
		//为什么String不用?
		//1.String本身是final的不可修改的
		//2.String是放在常量池里的,所以当克隆对象的某一String属性改变时,会直接在常量池中多出这个值,而不会影响到原来的String。
		p1.setName("muziliuri");
		System.out.println(p2.getName());
	}
}

构建者模式

用于构建复杂对象,如果一个类很复杂,构造方法不好写,那干脆写一个builder吧。

假设有如下Person类:

package builder;

public class Person {
	public String name;
	public int age;
	public String sex;
	public String job;
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", sex=" + sex + ", job=" + job + "]";
	}
}

我们为他写一个builder:

package builder;

public class PersonBuilder {
	private Person p = new Person();
	public PersonBuilder buildName(String name) {
		p.name = name;
		return this;
	}
	public PersonBuilder buildAge(int age) {
		p.age = age;
		return this;
	}
	public PersonBuilder buildSex(String sex) {
		p.sex = sex;
		return this;
	}
	public PersonBuilder buildJob(String job) {
		p.job = job;
		return this;
	}
	public Person build() {
		return p;
	}
}

我们就可以通过PersonBuilder来创建一个Person对象:

package builder;

public class Main {
	/**
	 * 构建者模式:用于构建复杂对象;
	 * 例如一个类有很多属性,构造参数不好写,有很多,那就写一个builder吧
	 */
	public static void main(String[] args) {
		Person ljj = new PersonBuilder()
				.buildAge(18)
				.buildJob("programmer")
				.buildName("ljj")
				.buildSex("sex")
				.build();
		System.out.println(ljj);
	}
}

工厂方法模式

假设我们要生产交通工具,那么我们就可以建一个生产交通工具的工厂,让工厂来帮我们产生交通工具,这就是简单工厂。但这种方法是不好的,因为一旦有了新的交通工具,我们就必须修改工厂的代码。

下面要说的十分重要,是我的一个感悟,就是写好的代码最好不要因为需求的变化而修改。你可以这样简单地理解开闭原则只需新增,不许修改

工厂方法就是这样改进了简单工厂,我们把生产每一类的交通工具的部门都独立出来,以后要新增一个交通工具,多写一个类就行了,不用修改其他代码。

可移动的接口:

package factorymethod;

public interface Movable {
	void go();
}

轿车:

package factorymethod;

public class Car implements Movable{

	@Override
	public void go() {
		System.out.println("小车跑起来");
	}

}

飞机:

package factorymethod;

public class Plane implements Movable{

	@Override
	public void go() {
		System.out.println("飞机起飞咯");
	}

}

火车:

package factorymethod;

public class Train implements Movable{

	@Override
	public void go() {
		System.out.println("列车即将到站");
	}

}

生产交通工具的工厂:

package factorymethod;

public abstract class VehicleFactory {
	public abstract Movable create();
}

轿车工厂:

package factorymethod;

public class CarFactory extends VehicleFactory{

	@Override
	public Movable create() {
		return new Car();
	}

}

package factorymethod;

public class PlaneFactory extends VehicleFactory{

	@Override
	public Movable create() {
		return new Plane();
	}

}

工厂:

package factorymethod;

public class TrainFactory extends VehicleFactory{

	@Override
	public Movable create() {
		return new Train();
	}

}

客户端调用:

package factorymethod;

public class Main {
	/**
	 * 为什么工厂方法比简单工厂要好呢?
	 * 简单工厂就是我就一个工厂,他能生产所有交通工具;
	 * 这样的话,如果新增一种交通工具,那么我就必须修改简单工厂的代码;
	 * 不符合开闭原则;
	 * 只许新增,不许修改!
	 */
	public static void main(String[] args) {
		Movable car = new CarFactory().create();
		car.go();
	}
}

抽象工厂模式

有些工厂很大,他要生产一系列的产品,而不仅仅是一类产品,这就诞生了工厂方法模式的强化班,抽象工厂模式。工厂方法模式可以看做是抽象工厂模式在产品类型为1时的特例。

假如我们这个工厂它不仅生产交通工具,他还生产食物,那么这个工厂就得这么写:

package abstractfactory;

public abstract class AbstractFactory {
	public abstract Food createFood();
	public abstract Vehicle createVihecle();
}

下面是食物以及它的实现类面包:

package abstractfactory;

public abstract class Food {
	public abstract void showName(); 
}

package abstractfactory;

public class Bread extends Food{

	@Override
	public void showName() {
		System.out.println("Bread");
	}

}

下面是交通工具以及它的实现类轿车:

package abstractfactory;

public abstract class Vehicle {
	public abstract void go();
}

package abstractfactory;

public class Car extends Vehicle{

	@Override
	public void go() {
		System.out.println("小车跑起来");
	}

}

有一个具体的工厂,它只生产面包和轿车:

package abstractfactory;

public class ConcreteFactory extends AbstractFactory{

	@Override
	public Food createFood() {
		return new Bread();
	}

	@Override
	public Vehicle createVihecle() {
		return new Car();
	}

}

客户端调用:

package abstractfactory;

public class Main {
	/**
	 * 抽象工厂方法使得我们可以生产产品簇,是工厂方法模式的加强版;
	 * 工厂方法模式可看作是产品簇中只有一类产品;
	 * 简单来说,就是有一个抽象的工厂,生产一下抽象的产品;
	 * 还有一个具体的工厂,继承了抽象工厂,生产的产品是继承了抽象产品的具体产品;
	 */
	public static void main(String[] args) {
		AbstractFactory factory = new ConcreteFactory();
		Food food = factory.createFood();
		food.showName();
		Vehicle vehicle = factory.createVihecle();
		vehicle.go();
	}
}

结构型模型

这类设计模式关注于类与对象间的组合关系。

组合模式

适用于树形结构,看到树形结构用它就对了。

我们就以文件夹和文件为例:

文件夹与文件的抽象:

package composite;

public abstract class File {
	protected String name;
	public abstract void printName();
}

文件:

package composite;

public class Document extends File{

	public Document(String name) {
		this.name = name;
	}
	@Override
	public void printName() {
		System.out.println("Document:" + this.name);
	}

}

文件夹:

package composite;

import java.util.ArrayList;
import java.util.List;

public class Folder extends File{

	private List<File> files = new ArrayList<File>();
	
	public Folder(String name) {
		this.name = name;
	}
	@Override
	public void printName() {
		System.out.println("Folder:" + this.name);
	}
	
	public void add(File file) {
		this.files.add(file);
	}
	
	public List<File> getFiles() {
		return this.files;
	}

}

客户端:

package composite;

public class Main {
	/**
	 * 组合模式:用于处理树状结构;
	 * 此处用文件和文件夹举例;
	 */
	public static void main(String[] args) {
		Folder folder1 = new Folder("根目录");
		Folder folder2 = new Folder("文件夹1");
		Folder folder3 = new Folder("文件夹2");
		Folder folder4 = new Folder("文件夹3");
		Document document1 = new Document("文件1");
		Document document2 = new Document("文件2");
		Document document3 = new Document("文件3");
		Document document4 = new Document("文件4");
		Document document5 = new Document("文件5");
		Document document6 = new Document("文件6");
		folder1.add(document1);
		folder1.add(document3);
		folder1.add(document6);
		folder1.add(folder2);
		folder2.add(folder3);
		folder2.add(folder4);
		folder3.add(document5);
		folder4.add(document2);
		folder4.add(document4);
		print(folder1,0);
	}
	
	public static void print(File file,int dep) {
		for (int i = 0; i < dep; i++) {
			System.out.print("--");
		}
		file.printName();
		if (file instanceof Folder) {
			((Folder) file).getFiles().forEach(f -> print(f,dep + 1));
		}
	}
}

适配器模式

电脑插不了HDMI,给他一个转接头就行了。

适配器模式就是给类添加一个转接口,让他实现未实现的接口。

比如我们希望狗能飞:

package adapter;

public interface Flightable {
	void fly();
}

package adapter;

public class Dog {
	public void bark() {
		System.out.println("汪汪汪!");
	}
}

我们搞一个狗的飞行适配器:

package adapter;

public class DogFlyAdapter extends Dog implements Flightable{

	@Override
	public void fly() {
		System.out.println("脚底加上风火轮");
		this.bark();
		System.out.println("起飞~");
	}

}

这样狗就能飞了:

package adapter;

public class Main {
	/**
	 * 适配器模式:用于对接不同接口;
	 * 典型的就是JavaIO中的InputStreamReader,将字节流和字符流对接了起来;
	 */
	public static void main(String[] args) {
		//如果我们希望狗能飞,就给他加一个飞行的适配器吧
		DogFlyAdapter dog = new DogFlyAdapter();
		dog.fly();
	}
}

桥接模式

分离抽象与实现,避免了类数量太多的问题。

看代码你就明白了。

具体礼物的抽象:

package bridge;

public abstract class ConcreteGift {
	public abstract void print();
}

具体礼物的实现:

package bridge;

public class Book extends ConcreteGift{

	@Override
	public void print() {
		System.out.println("书");
	}

}

package bridge;

public class Flower extends ConcreteGift{

	@Override
	public void print() {
		System.out.println("花");
	}

}

抽象礼物的抽象(聚合具体礼物):

package bridge;

public abstract class AbstractGift {
	protected ConcreteGift concreteGift;
	public AbstractGift(ConcreteGift concreteGift) {
		this.concreteGift = concreteGift;
	}
	public abstract void describe();
}

抽象礼物的实现:

package bridge;

public class ColdGift extends AbstractGift {

	public ColdGift(ConcreteGift concreteGift) {
		super(concreteGift);
	}

	@Override
	public void describe() {
		System.out.print("冷酷的");
		this.concreteGift.print();
	}

}

package bridge;

public class WarmGift extends AbstractGift{
	
	public WarmGift(ConcreteGift concreteGift) {
		super(concreteGift);
	}

	@Override
	public void describe() {
		System.out.print("温暖的");
		this.concreteGift.print();
	}

}

客户端:

package bridge;

public class Main {
	/**
	 * 桥接模式:解耦了抽象与实现,将实现聚合在抽象之中,避免了类数量过多的问题;
	 */
	public static void main(String[] args) {
		WarmGift gift = new WarmGift(new Book());
		gift.describe();
	}
}

装饰模式

这个是比较常用的,我们要增强有一个类的方法往往就可以加一个装饰器。JavaIO中的处理流都是节点流的装饰器。

形状的抽象以及它的实现:

package decorator;

public abstract class Shape {
	public abstract void draw();
}

package decorator;

public class Circle extends Shape{

	@Override
	public void draw() {
		System.out.println("圆");
	}

}

package decorator;

public class Rectangle extends Shape{

	@Override
	public void draw() {
		System.out.println("矩形");
	}

}

我们希望某一个图形在画出来的时候还能画出颜色,就可以增加一个装饰器来增强draw方法:

package decorator;

public abstract class ShapeDecorator extends Shape{
	protected Shape shape;
	public ShapeDecorator(Shape shape) {
		this.shape = shape;
	}
	
	@Override
	public void draw() {
		this.shape.draw();
	}
		
}

package decorator;

public class RedShapeDecorator extends ShapeDecorator{

	public RedShapeDecorator(Shape shape) {
		super(shape);
	}
	
	@Override
	public void draw() {
		printColor();
		super.draw();
	}
	
	public void printColor() {
		System.out.print("红色的");
	}

}

客户端调用:

package decorator;

public class Main {
	/**
	 * 装饰模式:主要用于对一个类的功能进行扩展;
	 * 最常见的例子就是IO流里面的处理流,就是对节点流进行的功能扩展;
	 */
	public static void main(String[] args) {
		Shape decorator = new RedShapeDecorator(new Circle());
		decorator.draw();
	}
}

代理模式

静态代理跟装饰模式就是一回事儿;

有如下类ljj:

package proxy;

public interface Programmer {
	public abstract void coding();
}

package proxy;

import java.util.Random;

public class Ljj implements Programmer{

	@Override
	public void coding() {
		System.out.println("李晶晶正在写代码");
		try {
			Thread.sleep(new Random().nextInt(10000));
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}

我们希望记录Ljj写了多长时间的代码,记住只许新增不许修改,那么我们就可以做一个代理类来完成这项任务:

package proxy;

public class LjjProxy implements Programmer{
	private Programmer programmer;
	public LjjProxy(Programmer programmer) {
		this.programmer = programmer;
	}
	@Override
	public void coding() {
		long start = System.currentTimeMillis();
		this.programmer.coding();
		long end = System.currentTimeMillis();
		System.out.println("代码写了" + (end - start)/1000 + "秒");
	}

}

这个Proxy类并不一定是必需的,因为我们可以使用动态代理来完成代理,且可以代理任何对象。无论是jdk的动态代理还是cglib的动态代理,本质都是通过反射拿到这个类的信息,动态创建代理类完成代理。

package proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class Main {

	/**
	 * 代理模式分为静态代理和动态代理。
	 */
	public static void main(String[] args) {
		//静态代理
		//基本就和装饰模式一样,只是语义上的不同。
		Programmer programmer = new LjjProxy(new Ljj());
		programmer.coding();
		
		//动态代理:我希望代理任何对象,即Object;
		//可以用反射做到这一点,因为只要类加载到内存,反射就可以拿到这个类的信息;
		//jdk动态代理:
		// newProxyInstance方法的第一个参数:被代理对象的类加载器;
		// 第二个参数:被代理对象实现的接口;
		// 第三个参数:代理对象处理逻辑;
		Ljj ljj = new Ljj();
		Programmer programmer2 = (Programmer) Proxy.newProxyInstance(
				Ljj.class.getClassLoader(),
				new Class[]{Programmer.class},
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						long start = System.currentTimeMillis();
						Object o = method.invoke(ljj, args);
						long end = System.currentTimeMillis();
						System.out.println("代码写了" + (end - start)/1000 + "秒");
						return o;
					}
				}
			);
		programmer2.coding();
		
		
		//cglib动态代理:被代理对象无需实现任何接口,Ljj2未实现Programmer接口。
		//但是cglib代理也有缺点,由于cglib代理类是被代理类的子类,所以对于final修饰的类无法使用cglib动态代理
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(Ljj2.class);
		enhancer.setCallback(new MethodInterceptor() {
			
			@Override
			public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
				long start = System.currentTimeMillis();
				Object o = arg3.invokeSuper(arg0, arg2);
				long end = System.currentTimeMillis();
				System.out.println("代码写了" + (end - start)/1000 + "秒");
				return o;
			}
		});
		Ljj2 ljj2 = (Ljj2) enhancer.create();
		ljj2.coding();
	}
}

享元模式

很多对象是可以重复利用的,享元模式就是池的概念,重复利用,节约空间。

package flyweight;

import java.util.HashMap;
import java.util.Map;


public class Main {
	/**
	 * 享元模式;
	 * 就是重复利用对象,池技术;
	 * 例如String常量池中,数据库连接池,线程池等等技术都是享元模式的应用;
	 * 下面是个小例子,但正是享元模式的本质;
	 */
	public static void main(String[] args) {
		Map<String,String> pool = new HashMap<String, String>();
		pool.put("aa", "AA");
		pool.put("bb", "BB");
		pool.put("cc", "CC");
		pool.put("dd", "DD");
		pool.put("ee", "EE");
		pool.put("ff", "FF");
		
		//从池里拿东西而不是每次都新建
		String str = pool.containsKey("aa") ? pool.get("aa") : "AA";
		System.out.println(str);
	}
}

外观模式

又叫门面模式,很简单,就是封装一下复杂系统,给客户端方便使用;

这里给出一个例子,Facade封装了Bank和SuperMarket,使得客户端不必知道银行和超市的存在也能取钱、购物:

package facade;

public class Bank {
	public void takeMoney() {
		System.out.println("取钱");
	}
}

package facade;

public class SuperMarket {
	public void shopping() {
		System.out.println("购物");
	}
}

package facade;

public class Facade {
	private Bank bank = new Bank();
	private SuperMarket market = new SuperMarket();
	public void takeMoney() {
		bank.takeMoney();
	}
	public void shopping() {
		market.shopping();
	}
}

客户端:

package facade;

public class Main {
	/**
	 * 门面模式简单来说就是对复杂系统的封装;
	 * 比如去政府部门办业务,总是要到各个部门跑来跑去,很麻烦;
	 * 我们可以封装一个门面,让人直接去这一个地方操作;
	 */
	public static void main(String[] args) {
		Facade facade = new Facade();
		facade.takeMoney();
		facade.shopping();
	}
}

行为型模式

此类模式关注于类和对象之间的交互;

策略模式

根据不同的策略来执行;

看代码:

package strategy;

public interface B {
	void b();
}

package strategy;

public class A {
	public void a(B b) {
		b.b();
	}
}

客户端:

package strategy;

import java.util.ArrayList;
import java.util.List;

public class Main {
	/**
	 * 策略模式其实很简单,假设有一个类A,它有一个方法a();
	 * 方法a的参数是一个不确定的类,即我们希望它比较健壮,对很多类都能试用;
	 * 这时候就可以用策略模式,不同的类实现同一个接口,a()的参数就是传一个接口的实现类;
	 * 这就是多态的魅力吧;
	 */
	public static void main(String[] args) {
		A a1 = new A();
		a1.a(new B() {
			@Override
			public void b() {
				System.out.println("1");
			}
		});
		// 当然我们可以使用lambda简写
		A a2 = new A();
		a2.a(() -> System.out.println("2"));
		
		
		//这就是策略模式,最经典的应用就是java中的compartor接口
		List<Integer> numList = new ArrayList<Integer>();
		numList.add(1);
		numList.add(9);
		numList.add(7);
		numList.add(6);
		List<String> strList = new ArrayList<String>();
		strList.add("1");
		strList.add("11111111");
		strList.add("11111");
		strList.add("111");
		
		numList.sort((num1,num2) -> num1 - num2);
		strList.sort((str1,str2) -> str1.length() - str2.length());
		
		System.out.println(numList);
		System.out.println(strList);
		
		//这就是多态的魅力吧
	}
}

访问者模式

有访问者和被访问者两个角色,关于如何访问的代码写在了访问者一方,被访问者负责接收访问者的访问即可,这就要求被访问者最好是有限固定的,不然当新增一个被访问者时,在访问者一方就需要更改代码。

这里举一个例子,我要访问我的电脑。

硬件及其实现类:

package visitor;

public interface Hardware {
	void accept(Visitor visitor);
}

package visitor;

public class Cpu implements Hardware{

	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}

}

package visitor;

public class Memory implements Hardware{

	@Override
	public void accept(Visitor visitor) {
		visitor.visit(this);
	}

}

package visitor;

public class Computer implements Hardware{

	private Hardware[] hardwares = {new Cpu(),new Memory()};
	@Override
	public void accept(Visitor visitor) {
		for (int i = 0; i < hardwares.length; i++) {
			hardwares[i].accept(visitor);
		}
		visitor.visit(this);
	}

}

我实现访问者:

package visitor;

public interface Visitor {
	void visit(Cpu cpu);
	void visit(Memory memory);
	void visit(Computer computer);
}

package visitor;

public class Ljj implements Visitor{

	@Override
	public void visit(Cpu cpu) {
		System.out.println("Ljj访问CPU");
	}

	@Override
	public void visit(Memory memory) {
		System.out.println("Ljj访问内存");
	}

	@Override
	public void visit(Computer computer) {
		System.out.println("Ljj访问整台电脑");
	}

}

客户端:

package visitor;

public class Main {
	/**
	 * 访问者模式:有访问者和被访问者;
	 * 不同的访问者会按照不同的方式访问,那么访问的逻辑是写在访问者那边,还是被访问者那边呢?
	 * 访问者模式给出的答案是写在访问者那边,被访问者负责接收访问;
	 * 这样被访问者就不用判断来访者是谁。
	 * 同时这就要求被访问者最好不要扩展,因为这样的话访问者那边的代码就又要修改;
	 * 因此访问者模式适用于结构固定不变的对象;
	 */
	public static void main(String[] args) {
		Visitor visitor = new Ljj();
		Hardware computer = new Computer();
		computer.accept(visitor);
		
	}
}

状态模式

根据不同状态执行不同的操作,和策略模式差不多。

下面的例子是电脑根据不同的状态执行计算操作:

状态及其实现:

package state;

public interface State {
	void exec();
}

package state;

public class StopState implements State{

	@Override
	public void exec() {
		System.out.println("设备已关闭,无法计算");
	}

}

package state;

public class RunningState implements State{

	@Override
	public void exec() {
		System.out.println("开始计算");
	}

}

电脑:

package state;

public class Computer {
	private State state;

	public Computer(State state) {
		this.state = state;
	}
	public State getState() {
		return state;
	}

	public void setState(State state) {
		this.state = state;
	}
	
	public void compute() {
		this.state.exec();
	}
}

客户端:

package state;

public class Main {
	/**
	 * 根据状态不同进行不同的操作,跟策略模式很像;
	 */
	public static void main(String[] args) {
		Computer computer = new Computer(new RunningState());
		computer.compute();
		computer.setState(new StopState());
		computer.compute();
	}
}

责任链模式

一连串的执行操作,本质就是个线性表。

这里给出一个demo,事实上责任链模式变化很多,比如执行到某一处停止往下操作等;

servlet中的Filter就是责任链模式的典型应用;

package chain;

public abstract class Handler {
	private Handler next;
	public void setNext(Handler next) {
		this.next = next;
	}
	public Handler getNext() {
		return this.next;
	}
	public abstract void handle();
}

package chain;

public class ConcreteHandler1 extends Handler{

	@Override
	public void handle() {
		System.out.println("第一个节点完成处理");
		if (super.getNext() != null) {
			super.getNext().handle();
		}
	}

}

package chain;

public class ConcreteHandler2 extends Handler{

	@Override
	public void handle() {
		System.out.println("第二个节点完成处理");
		if (super.getNext()!=null) {
			super.getNext().handle();
		}
	}

}

客户端:

package chain;

public class Main {
	/**
	 * 责任链模式:说白了就是模拟链表,使得每一个节点依次处理请求;
	 */
	public static void main(String[] args) {
		Handler handler1 = new ConcreteHandler1();
		Handler handler2 = new ConcreteHandler2();
		handler1.setNext(handler2);
		handler1.handle();
	}
}

观察者模式

有一堆观察者和一个被观察者,一旦某事发生,观察者就会执行对应操作。

往往观察者需要知道被观察者是谁,被观察者也得知道他有哪些观察者,所以你中有我我中有你;

比较复杂时,可以抽象出事件,观察者只需知道事件即可,事件来聚合事件源(被观察者);

观察者的抽象和实现:

package observer;

public abstract class Observer {
	protected Source source;
	public abstract void handle();
}

package observer;

public class ConcreteObserver1 extends Observer{

	public ConcreteObserver1(Source source) {
		this.source = source;
		this.source.attach(this);
	}
	
	@Override
	public void handle() {
		System.out.println("观察者1处理事件");
	}

}

package observer;

public class ConcreteObserver2 extends Observer{

	public ConcreteObserver2(Source source) {
		this.source = source;
		this.source.attach(this);
	}
	
	@Override
	public void handle() {
		System.out.println("观察者2处理事件");
	}

}

事件源(被观察者):

package observer;

import java.util.ArrayList;
import java.util.List;

public class Source {
	private List<Observer> observers = new ArrayList<Observer>();
	
	public void attach(Observer observer) {
		this.observers.add(observer);
	}
	
	public void notifyObservers() {
		this.observers.forEach(observer -> observer.handle());
	}
}

客户端:

package observer;

public class Main {
	/**
	 * 观察者模式:有三个要素,观察者,事件源,和事件本身;
	 * 事件本身其实也可以封装出一个类来;
	 * 事件源中需要有多个观察者;
	 * 观察者需要知道事件源是谁;
	 */
	public static void main(String[] args) {
		Source source = new Source();
		new ConcreteObserver1(source);
		new ConcreteObserver2(source);
		source.notifyObservers();
	}
}

模板方法模式

这个比较简单,就是父类定义好一套方法的执行顺序并定义为一个模板方法,子类实现不同方法即可;

看代码就很明白了:

父:

package template;

public abstract class A {
    //模板方法
	public void a() {
		b();
		c();
	}
	public abstract void b();
	public abstract void c();
}

子:

package template;

public class B extends A{

	@Override
	public void b() {
		System.out.println("first step");
	}

	@Override
	public void c() {
		System.out.println("second step");
	}

}

客户端:

package template;

public class Main {
	/**
	 * 模板方法模式:父类定义好方法的执行模板,子类具体实现;
	 */
	public static void main(String[] args) {
		A a = new B();
		a.a();
	}
}

备忘录模式

就是给对象做备份的,当需要的时候可以回到过去;

备忘录:

package memento;

public class Memento {
	private int state;
	public Memento(int state) {
		this.state = state;
	}
	public int getState() {
		return this.state;
	}
}

需要备份的对象:

package memento;

public class Originator {
	private int state;

	public int getState() {
		return state;
	}

	public void setState(int state) {
		this.state = state;
	}
	
	public void show() {
		System.out.println(this.state);
	}
	
	public Memento createMemento() {
		return new Memento(state);
	}
	
	public void setMemento(Memento memento) {
		this.state = memento.getState();
	}
}

客户端:

package memento;

public class Main {
	/**
	 * 备忘录模式:就是备份;
	 */
	public static void main(String[] args) {
		Originator originator = new Originator();
		Memento memento = originator.createMemento();
		originator.setState(2);
		originator.show();
		originator.setMemento(memento);
		originator.show();
	}
}

命令模式

同样是一个非常简单的模式,有命令的发起者和命令的执行者两个角色。为了解耦他们,抽象出命令这个角色。

命令执行者:

package command;

public class Receiver {
	public void run() {
		System.out.println("执行指令");
	}
}

命令及其实现:

package command;

public interface Command {
	void exec();
}

package command;

public class ConcreteCommand implements Command{

	private Receiver receiver;
	public ConcreteCommand(Receiver receiver) {
		this.receiver = receiver;
	}
	@Override
	public void exec() {
		receiver.run();
	}

}

命令的发起者:

package command;

public class Invoker {
	private Command command;
	public Invoker(Command command) {
		this.command = command;
	}
	public void call() {
		this.command.exec();
	}
}

客户端:

package command;

public class Main {
	/**
	 * 命令模式:抽象出命令,解耦命令发起者和执行者;
	 */
	public static void main(String[] args) {
		//命令
		Command command = new ConcreteCommand(new Receiver());
		//命令发起者
		Invoker invoker = new Invoker(command);
		//发出指令
		invoker.call();
	}
}

迭代器模式

不同的数据结构有不同的遍历方式,面对一个未知的数据结构,怎么知道如何遍历他呢?

如果他实现了迭代器,那么你就可以通过迭代器来遍历他。

java帮我们封装好了迭代器的接口,简化一下大概长这样:

public interface Iterator<E> {
    boolean hasNext();
    E next();
}


我们要知道当前遍历到的有没有下一个了的操作hasNext,以及获取下一个的操作next。

这里我们封装一个自己的ArrayList,实现迭代器,通过迭代器来遍历他。

package iterator;

import java.util.Iterator;

public class MyArray<E> {
	private Object[] o = new Object[10];
	private int size = 0;
	public MyArrayIterator iterator = new MyArrayIterator();
	public void add(E element) {
		if (size == o.length) {
			Object[] o2 = new Object[size * 2];
			System.arraycopy(o, 0, o2, 0, size);
			o = o2;
		}
		o[size] = element;
		size++;
	}
	public int size() {
		return size;
	}
	private class MyArrayIterator implements Iterator<E> {

		private int index = 0;
		@Override
		public boolean hasNext() {
			return index < size ? true : false;
		}

		@Override
		public E next() {
			index++;
			return (E) o[index-1];
		}
		
	}
}

客户端:

package iterator;

import java.util.Iterator;

public class Main {
	/**
	 * 迭代器模式:就是用来做容器遍历的;
	 * 如果对所有容器都实现迭代器接口,那么使用者只要按照迭代器来遍历就行了;
	 */
	public static void main(String[] args) {
		MyArray<String> array = new MyArray<String>();
		array.add("1");
		array.add("2");
		array.add("3");
		array.add("4");
		array.add("5");
		array.add("6");
		array.add("7");
		Iterator<String> iterator = array.iterator;
		while (iterator.hasNext()) {
			String str = iterator.next();
			System.out.println(str);
		}
	}
}

调停者模式

又叫中介者模式,如果各个不同类之间相互依赖,这样关系就很复杂。我们不如抽象出一个中介者,每个类通过中介者来作用于其他类。

没有代码,和外观模式代码一样。因为银行如果需要购物,他就可以通过Facade来实行;

所以如果你把客户端看做是内部成员,那外观模式就是调停者模式,仅仅是语义上的差别而已。

MQ正是调停者模式思想的体现。

package mediator;

public class Main {
	
	/**
	 * 调停者模式,与门面模式基本相同;
	 * 他是对内协调各个子系统之间的关系,也是解耦合;
	 * 例如mq其实就是调停者模式的代表;
	 */
}

解释器模式

用于解析脚本语言、正则等语言的模式,应用场景很少,就不聊了,也确实不会。

package interpreter;

public class Main {
	/**
	 * 解释器模型:一般用于解析语言,例如脚本语言、正则等;
	 * 用的太少,略过;
	 */
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值