设计模式
文章目录
日期: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 {
/**
* 解释器模型:一般用于解析语言,例如脚本语言、正则等;
* 用的太少,略过;
*/
}