24种设计模式
文章目录
设计原则
- 单一职责原则
- 一个类别太大,负责单一的职责
- 高内聚,低耦合
- 里氏替换原则
- 所有使用父类的地方,必须能够透明的使用子类。比如子类重写父类的A()方法之后,又重载了很多的A()方法,那么这样子会照成对父类接口方法的歧义
- 例:List list = new ArrayList(); 如果ArrayList中重载了父类很多的方法,并且都进行了重写,那么可能这种声明对象的方式能调用的方法就不能调用那些重写的方法。
- 依赖倒置原则
- 依赖抽象,而不是依赖具体
- 面向抽象编程
- 例:List list = new ArrayList();那么在具体实现中,如果我们需要将对象的声明变为 List list = new LinkedList() 的话,就不需要改动其他的代码。
- 接口隔离原则
- 每个接口负责自己该干的事,而不要吧所有职责都放在同一个接口当中来进行使用
- 迪米特法则
- 尽量不要和陌生人说话
- 非陌生人
- this
- 方法对象参数
- 成员对象
- 成员集合对象中的对象元素集合
- 当前对象所创建的对象
- 陌生人
- 如在一个方法体中临时创建的一个新的对象,在上面枚举出来的非陌生人列表中没有出现过
单例模式
- 懒汉式和饿汉式
- 线程安全模式
- 枚举实现
例:配置文件的读取
工厂模式
- 普通模式:适合同一种的产品
- 抽象工厂:适合产品族
例:创建坦克和子弹
策略模式
- Comparable 和 Comparator
例:子弹的发射方式
import java.util.Arrays;
import java.util.Comparator;
public class Designer {
public static void main(String[] args) {
//int[] a = {9, 2, 3, 5, 7, 1, 4};
Cat[] a = {new Cat(3, 3), new Cat(5, 5), new Cat(1, 1)};
//Dog[] a = {new Dog(3), new Dog(5), new Dog(1)};
Sorter<Cat> sorter = new Sorter<>();
sorter.sort(a, (o1, o2)->{
if(o1.weight < o2.weight) return -1;
else if (o1.weight>o2.weight) return 1;
else return 0;
});
System.out.println(Arrays.toString(a));
}
}
class Sorter<T> {
public void sort(T[] arr, Comparator<T> comparator) {
for(int i=0; i<arr.length - 1; i++) {
int minPos = i;
for(int j=i+1; j<arr.length; j++) {
minPos = comparator.compare(arr[j],arr[minPos])==-1 ? j : minPos;
}
swap(arr, i, minPos);
}
}
//sort(int)
void swap(T[] arr, int i, int j) {
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
class Cat implements Comparable<Cat> {
int weight, height;
public Cat(int weight, int height) {
this.weight = weight;
this.height = height;
}
public int compareTo(Cat c) {
if(this.weight < c.weight) return -1;
else if(this.weight > c.weight) return 1;
else return 0;
}
@Override
public String toString() {
return "Cat{" +
"weight=" + weight +
", height=" + height +
'}';
}
}
门面模式
- MVC模型中,Model和View进行分离,职责更加确定
例:MVC中,C控制层作为门面,Model和View都是直接和Controller层进行交互,客户端直接和Controller进行交互
调停者模式
- 多个类之间两两或者两者以上有关系,并且关系的类型相似,那么我们可以将这些关系都抽取出来作为调停者模式来解耦,使得之前的类之间没有之间关系而是和调停者产生关系
例:坦克和子弹碰撞,坦克和坦克碰撞。这种关系都是碰撞,我们就可以使用调停者来专门处理这种关系。
参考博客:https://www.jb51.net/article/63256.htm
责任链模式
- 处理同一类的操作,但是解决类操作会根据对象的不同有着不同的解决方案
例:坦克和子弹碰撞,坦克和坦克碰撞。都是碰撞,我们就可以从碰撞处理的集合中寻找对应的解决方案
装饰者模式
- 对原先的类进行装饰
例:坦克的颜色,大小,是否有装甲
饮料的装饰者模式应用
public class Designer {
public static void main(String[] args) {
Drink drink = new Coffee();
// 基本茶饮内容
drink.content();
// 加了调料的饮料
drink = new Sugar(drink);
drink = new Ice(drink);
drink.content();
}
}
interface Drink {
void content();
}
class Coffee implements Drink{
@Override
public void content() {
System.out.println("Basic Coffee");
}
}
interface DrinkDecoration extends Drink{
}
class Sugar implements DrinkDecoration {
private Drink drink;
public Sugar(Drink drink) {
this.drink = drink;
}
@Override
public void content() {
System.out.println("Add Sugar !");
}
}
class Ice implements DrinkDecoration {
private Drink drink;
public Ice(Drink drink) {
this.drink = drink;
}
@Override
public void content() {
System.out.println("Add Ice !");
}
}
JDK里面的IO
public class Main {
public static void main(String[] args) throws Exception {
File f = new File("c:/work/test.data");
FileOutputStream fos = new FileOutputStream(f);
OutputStreamWriter osw = new OutputStreamWriter(fos);
BufferedWriter bw = new BufferedWriter(osw);
bw.write("http://www.mashibing.com");
bw.flush();
bw.close();
}
}
观察者模式
- 对一些事件进行监听。组成部分:观察者、事件、被观察对象,观察者之和事件打交道,不和事件源打交道,实现观察者和时间源的解耦。
例:母亲对孩子睡眠状态的监听;坦克大战里面对键盘事件的监听
import java.util.ArrayList;
import java.util.List;
/**
* 有很多时候,观察者需要根据事件的具体情况来进行处理
* 大多数时候,我们处理事件的时候,需要事件源对象
* 事件也可以形成继承体系
*/
class Child {
private boolean cry = false;
private List<Observer> observers = new ArrayList<>();
{
observers.add(new Dad());
observers.add(new Mum());
observers.add(new Dog());
observers.add((e)-> System.out.println("ppp"));
//hook callback function
}
public boolean isCry() {
return cry;
}
public void wakeUp() {
cry = true;
wakeUpEvent event = new wakeUpEvent(System.currentTimeMillis(), "bed", this);
for(Observer o : observers) {
o.actionOnWakeUp(event);
}
}
}
abstract class Event<T> {
abstract T getSource();
}
class wakeUpEvent extends Event<Child>{
long timestamp;
String loc;
Child source;
public wakeUpEvent(long timestamp, String loc, Child source) {
this.timestamp = timestamp;
this.loc = loc;
this.source = source;
}
@Override
Child getSource() {
return source;
}
}
interface Observer {
void actionOnWakeUp(wakeUpEvent event);
}
class Dad implements Observer {
public void feed() {
System.out.println("dad feeding...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
feed();
}
}
class Mum implements Observer {
public void hug() {
System.out.println("mum hugging...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
hug();
}
}
class Dog implements Observer {
public void wang() {
System.out.println("dog wang...");
}
@Override
public void actionOnWakeUp(wakeUpEvent event) {
wang();
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
//do sth
c.wakeUp();
}
}
组合模式
- 比如文件夹和文件的关系,逻辑就是文件夹下面可以有文件或者文件夹,文件夹下面的文件夹里面又可以有文件或者文件夹。
例:树枝和叶子,可以设置成对象,多种类型相互嵌套
import java.util.ArrayList;
import java.util.List;
abstract class Node {
abstract public void p();
}
class LeafNode extends Node {
String content;
public LeafNode(String content) {this.content = content;}
@Override
public void p() {
System.out.println(content);
}
}
class BranchNode extends Node {
List<Node> nodes = new ArrayList<>();
String name;
public BranchNode(String name) {this.name = name;}
@Override
public void p() {
System.out.println(name);
}
public void add(Node n) {
nodes.add(n);
}
}
public class Main {
public static void main(String[] args) {
BranchNode root = new BranchNode("root");
BranchNode chapter1 = new BranchNode("chapter1");
BranchNode chapter2 = new BranchNode("chapter2");
Node r1 = new LeafNode("r1");
Node c11 = new LeafNode("c11");
Node c12 = new LeafNode("c12");
BranchNode b21 = new BranchNode("section21");
Node c211 = new LeafNode("c211");
Node c212 = new LeafNode("c212");
root.add(chapter1);
root.add(chapter2);
root.add(r1);
chapter1.add(c11);
chapter1.add(c12);
chapter2.add(b21);
b21.add(c211);
b21.add(c212);
tree(root, 0);
}
static void tree(Node b, int depth) {
for(int i=0; i<depth; i++) System.out.print("--");
b.p();
if(b instanceof BranchNode) {
for (Node n : ((BranchNode)b).nodes) {
tree(n, depth + 1);
}
}
}
}
享元模式
- 线程池、String用的就是享元模式,一般情况下要用就去拿即可
例:String中的字符串就是放在常量池中
结合composite
静态代理
- 对原来的方法进行加强。和装饰者模式和委托模式比较类似,主要区别就是语义的不同。
例:对原先的类进行日志加强。方法执行前日志的打印,方法执行后日志的打印。
public class Tank implements Movable {
/**
* 模拟坦克移动了一段儿时间
*/
@Override
public void move() {
System.out.println("Tank moving claclacla...");
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Tank t = new Tank();
TankTimeProxy ttp = new TankTimeProxy(t);
TankLogProxy tlp = new TankLogProxy(ttp);
tlp.move();
// new TankLogProxy(
// new TankTimeProxy(
// new Tank()
// )
// ).move();
}
}
class TankTimeProxy implements Movable {
Movable m;
public TankTimeProxy(Movable m) {
this.m = m;
}
@Override
public void move() {
long start = System.currentTimeMillis();
m.move();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
class TankLogProxy implements Movable {
Movable m;
public TankLogProxy(Movable m) {
this.m = m;
}
@Override
public void move() {
System.out.println("start moving...");
m.move();
long end = System.currentTimeMillis();
System.out.println("stopped!");
}
}
interface Movable {
void move();
}
动态代理
- 不需要进行额外的创建类就可以在程序运行期间生成代理类,也就是动态的产生代码
例:就是JDK自带的代理类,其中通过反射生成我们的代码
解释JDK的代理类:生成的代理类中有着以下的特点
- 其中会继承我们指定的接口
- 会通过反射实现我们指定接口中的方法
- 并且在里面会通过反射来进行方法的调用
- 这个动态生成的代理类会继承Proxy类,Proxy类中有着InvocationHandler的成员变量
- 代理类中会通过super调用Proxy类中InvocationHandler变量的Invoke方法(其中就是我们实现的处理器方法)
- 这个就是我们JDK动态代理实现的基本原理机制
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Designer {
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
// System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles","true");
Tank tank = new Tank();
Move move = (Move) Proxy.newProxyInstance(
//
Tank.class.getClassLoader()
// 接口中的方法是代理对象中会进行实现的方法
, new Class[]{Move.class}
, new InvocationHandler() {
// proxy 代表生成的代理类
// method 代表可以被进行加强的方法,指的就是接口中的方法
// args 方法传入的参数,当然参数可以是没有的
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
System.out.println("method" + methodName);
if ("attack".equals(methodName)) {
System.out.println("Pre Stronger");
method.invoke(tank, args);
System.out.println("Twice Strong");
}
return method;
}
}
);
move.attack();
}
}
interface Move {
void attack();
void attack2(int x,String str);
}
class Tank implements Move {
@Override
public void attack() {
System.out.println("Tank is attack !");
}
@Override
public void attack2(int x, String str) {
// TODO
// 叶之越, 2020/12/22
// Class: Tank
// Method:attack2
// return:0
}
public void other() {
System.out.println("Other methods ! ");
}
}
ASM:可以直接修改字节码文件的所有二进制
SpringAOP的切面:直接将指定我们需要切取出来的方法和需要进行切入的方法,然后就可以实现AOP了
迭代器模式
- 为了给所有容器实现一个通用的接口
例:JDK里面的Collection的一些子类实现
访问者模式
- 对不同的来访者使用不同的方式来实现,使用的场景比较少,用于一些结构固定的场景
例:电脑基本上都是有CPU、内存、硬盘,这个结构是基本固定的
public class Computer {
ComputerPart cpu = new CPU();
ComputerPart memory = new Memory();
ComputerPart board = new Board();
public void acccept(Visitor v) {
this.cpu.accept(v);
this.memory.accept(v);
this.board.accept(v);
}
public static void main(String[] args) {
PersonelVisitor p = new PersonelVisitor();
new Computer().acccept(p);
System.out.println(p.totalPrice);
}
}
abstract class ComputerPart {
abstract void accept(Visitor v);
//some other operations eg:getName getBrand
abstract double getPrice();
}
class CPU extends ComputerPart {
@Override
void accept(Visitor v) {
v.visitCpu(this);
}
@Override
double getPrice() {
return 500;
}
}
class Memory extends ComputerPart {
@Override
void accept(Visitor v) {
v.visitMemory(this);
}
@Override
double getPrice() {
return 300;
}
}
class Board extends ComputerPart {
@Override
void accept(Visitor v) {
v.visitBoard(this);
}
@Override
double getPrice() {
return 200;
}
}
interface Visitor {
void visitCpu(CPU cpu);
void visitMemory(Memory memory);
void visitBoard(Board board);
}
class PersonelVisitor implements Visitor {
double totalPrice = 0.0;
@Override
public void visitCpu(CPU cpu) {
totalPrice += cpu.getPrice()*0.9;
}
@Override
public void visitMemory(Memory memory) {
totalPrice += memory.getPrice()*0.85;
}
@Override
public void visitBoard(Board board) {
totalPrice += board.getPrice()*0.95;
}
}
class CorpVisitor implements Visitor {
double totalPrice = 0.0;
@Override
public void visitCpu(CPU cpu) {
totalPrice += cpu.getPrice()*0.6;
}
@Override
public void visitMemory(Memory memory) {
totalPrice += memory.getPrice()*0.75;
}
@Override
public void visitBoard(Board board) {
totalPrice += board.getPrice()*0.75;
}
}
构建者模式
- 分离复杂对象的构建
例:坦克游戏中各种复杂的地形,比如有的地形有很多墙,有的地形有很多湖泊。就可以通过构建者进行构建。
public interface TerrainBuilder {
TerrainBuilder buildWall();
TerrainBuilder buildFort();
TerrainBuilder buildMine();
Terrain build();
}
class Person {
int id;
String name;
int age;
double weight;
int score;
Location loc;
private Person() {}
public static class PersonBuilder {
Person p = new Person();
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 PersonBuilder loc(String street, String roomNo) {
p.loc = new Location(street, roomNo);
return this;
}
public Person build() {
return p;
}
}
}
class Location {
String street;
String roomNo;
public Location(String street, String roomNo) {
this.street = street;
this.roomNo = roomNo;
}
}
适配器模式
- 转换器,用于不同类之间的适配。和装饰者模式很像
例:VGA -> VGA转HDMI -> HDMI;JDK中的IO类;
桥接模式
- 拓展的东西非常多的地方会使用,并且可以不断进行分类。对于两个独立变化的维度,使用桥接模式再适合不过了。
public class Designer {
public static void main(String[] args) {
}
}
abstract class Gift {
GiftImpl impl;
}
class GiftImpl {
}
class Book extends GiftImpl {
}
class Flower extends GiftImpl {
}
class GG {
public void chase(MM mm) {
Gift g = new WarmGift(new Flower());
give(mm, g);
}
public void give(MM mm, Gift g) {
System.out.println(g + "gived!");
}
}
class MM {
String name;
}
class WarmGift extends Gift {
public WarmGift(GiftImpl impl) {
this.impl = impl;
}
}
class WildGift extends Gift {
public WildGift(GiftImpl impl) {
this.impl = impl;
}
}
命令模式
- 常见使用命令模式的场景就是撤销和执行操作,或者事务的原子性操作的实现
例:事务的处理就可能是一系列的命令同时失败或者成功。可以和责任链模式一起使用来执行一系列的撤销操作
import java.util.ArrayList;
import java.util.List;
public class Designer {
public static void main(String[] args) {
Content c = new Content();
Command insertCommand = new InsertCommand(c);
insertCommand.doit();
insertCommand.undo();
Command copyCommand = new CopyCommand(c);
insertCommand.doit();
insertCommand.undo();
Command deleteCommand = new DeleteCommand(c);
deleteCommand.doit();
deleteCommand.undo();
List<Command> commands = new ArrayList<>();
commands.add(new InsertCommand(c));
commands.add(new CopyCommand(c));
commands.add(new DeleteCommand(c));
for(Command comm : commands) {
comm.doit();
}
System.out.println(c.msg);
for(int i= commands.size()-1; i>=0; i--) {
commands.get(i).undo();
}
System.out.println(c.msg);
}
}
abstract class Command {
public abstract void doit(); //exec run
public abstract void undo();
}
class Content {
String msg = "hello everybody ";
}
class CopyCommand extends Command {
Content c;
public CopyCommand(Content c) {
this.c = c;
}
@Override
public void doit() {
c.msg = c.msg + c.msg;
}
@Override
public void undo() {
c.msg = c.msg.substring(0, c.msg.length()/2);
}
}
class DeleteCommand extends Command {
Content c;
String deleted;
public DeleteCommand(Content c) {
this.c = c;
}
@Override
public void doit() {
deleted = c.msg.substring(0, 5);
c.msg = c.msg.substring(5, c.msg.length());
}
@Override
public void undo() {
c.msg = deleted + c.msg;
}
}
class InsertCommand extends Command {
Content c;
String strToInsert = "http://www.mashibing.com";
public InsertCommand(Content c) {
this.c = c;
}
@Override
public void doit() {
c.msg = c.msg + strToInsert;
}
@Override
public void undo() {
c.msg = c.msg.substring(0, c.msg.length()-strToInsert.length());
}
}
原型模式
- 克隆。需要明白深克隆和浅克隆之间的区别。
例:Java中的Cloneable中的clone()方法,如果需要进行深克隆,那么就需要进行对对象里面的属性实现Cloneable接口并且重写其中的clone()方法。
模板方法
- 就是一系列的方法需要我们进行顺序的封装,然后就可以作为我们的方法模板来进行执行
状态模式
- 用于客户端的方法比较固定的情况下可以进行使用
例如:下图中的TCP的open()、close()方法
补充:有限状态机,简单来说就是状态的数量是有固定的一些场景,例如线程的状态
谈到的一些问题
- 当两个类产生关联的时候我们是直接定义成类中的字段还是定义成其中的一个方法变量比较好呢?
通常建立在方法中会减小类之间的耦合,但是可能会创建比较多的对象;定义成字段的话会减少对象的创建,增大耦合度。