文章目录
23种设计模式理解
创建型模式(5种)
工厂方法模式
工厂要制造的产品类如下 一个玩具狗 一个玩具猫都实现了玩具接口
interface Toy {
void say();
}
class ToyDog implements Toy {
public void say() {
System.out.println("wang wang");
}
}
class ToyCat implements Toy {
public void say() {
System.out.println("miao miao");
}
}
-
一个工厂
/** * 工厂方法 **/ class ToyFactory { public Toy getToy(String type) { if ("dog".equals(type)) { return new ToyDog(); } else if ("cat".equals(type)) { return new ToyCat(); } else { return null; } } } /** * 测试代码如下 **/ public class Solution { public static void main(String[] args) { ToyFactory factory = new ToyFactory(); Toy toy = factory.getToy("dog"); toy.say(); toy = factory.getToy("cat"); toy.say(); } }
输出结果如下
wang wang miao miao
-
多个工厂
一个工厂时如果要添加新玩具,需要修改原工厂代码,及其不方便。所以多个工厂代码如下。
/** * 工厂方法 **/ class ToyFactory { public Toy getToyDog() { return new ToyDog(); } public Toy getToyCat() { return new ToyCat(); } } /** * 测试代码如下 **/ public class Solution { public static void main(String[] args) { ToyFactory factory = new ToyFactory(); Toy toy = factory.getToyDog(); toy.say(); toy = factory.getToyDog(); toy.say(); } }
输出结果如下
wang wang miao miao
若想添加新产品可直接添加新方法。也可以给工厂类中方法加上static修饰这样可以不用实例化工厂类。
总结:一个工厂缺点是如果type类型输入无效无法创建有效玩具,多个工厂缺点是对于每个玩具都要写单独方法。且如果需要更新新玩具需要修改现有类代码,不利于扩展所以引入了抽象工厂模式。
抽象工厂模式
若不想更改现有工厂代码,可如下代码所示。
-
/** * 抽象工厂 **/ interface Factory { Toy getToy(); } /** * 工厂实例 **/ class DogFactory implements Factory { @Override public Toy getToy() { return new ToyDog(); } } class CatFactory implements Factory { @Override public Toy getToy() { return new ToyCat(); } } /** * 测试代码如下 **/ public class Solution { public static void main(String[] args) { Factory factory = new DogFactory(); Toy toy = factory.getToy(); toy.say(); factory = new CatFactory(); toy = factory.getToy(); toy.say(); } }
运行结果跟工厂模式一致,抽象工厂优点是便于扩展,如果想添加新玩具马,创建一个类实现Toy玩具类,创建一个工厂类生产玩具马即可。
总结:相较于工厂模式来说,抽象工厂对于产生新玩具只需要创建新类即可,对于扩展性方便。
建造者模式
工厂模式只生产对应的一个产品,建造者模式则是将产品的细节指定完整生成的产品。
例如生产键盘如果使用工厂模式则可以返回一个键盘,建造者模式则可以更加关注键盘的细节,如键盘使用的轴和键盘配色。而工厂模式更关心的是返回了一个键盘。
/**
* 生产的产品
**/
class KeyBoard {
private String axisName;
private String color;
public String getAxisName() {
return axisName;
}
public void setAxisName(String axisName) {
this.axisName = axisName;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
/**
* 建造者
**/
class Builder {
/**
* 产品1
**/
public KeyBoard getTypeOne() {
KeyBoard keyBoard = new KeyBoard();
keyBoard.setAxisName("黑轴");
keyBoard.setColor("黑色");
return keyBoard;
}
/**
* 产品2
**/
public KeyBoard getTypeTwo() {
KeyBoard keyBoard = new KeyBoard();
keyBoard.setAxisName("红轴");
keyBoard.setColor("绿色");
return keyBoard;
}
}
单例模式
有时候在java中一个类只需要有一个实例,如果出现多个实例会造成状态紊乱。
-
单线程单例
class SingleExample { private static HashMap map = null; public static Map getInstance() { if (map == null) { map = new HashMap(); } return map; } }
-
多线程单例
同时有多个线程同时获取,如果还按照上图代码执行会出现问题。因为一个对象创建分为3步:1、为对象分配空间。 2、初始化对象。3、将内存空间地址赋值给对应引用。 JVM不会禁止指令重排序也就是如果2 3步颠倒顺序,对象初始化一半则会被另一个线程获取引发异常。我们可以使用volatile标记此成员变量。
注意:volatile不会禁止2 3的重排序,他只会在写未完成时禁止下一次读。所以当第一个线程在初始化对象时,第二个线程想要读取这个对象是被禁止的,只有当写完成时才会被允许。
class SingleExample { private static volatile HashMap map = null; private static final Object object = new Object(); public static Map getInstance() { if (map == null) { synchronized (object) { if (map == null) { map = new HashMap(); } } } return map; } }
总结:多线程单例模式也就是常说的DCL(Double Check Locking),单线程下程序只会顺序执行所以不会出错。
原型模式
将一个对象创建一份新的并返回,例如复印机会把一份复制成相同的两份。
在Java中 只需要实现Cloneable接口并重写clone()方法即可。
class SingleExample implements Cloneable {
@Override
public Object clone() throws CloneNotSupportedException {
SingleExample singleExample = (SingleExample) super.clone();
return singleExample;
}
}
注意:Java中clone方法实现的是浅复制。
浅复制:将一个对象复制后,基本数据类型的变量都会重新创建,而引用类型,指向的还是原对象所指向的。
深复制:将一个对象复制后,不论是基本数据类型还有引用类型,都是重新创建的。简单来说,就是深复制进行了完全彻底的复制,而浅复制不彻底。
结构型模式(7种)
适配器模式
将某个类的接口转换成另一个类需要的接口,因为原先类不支持想要的新操作,但是还需要保留之前类的老操作。
-
类适配器模式
/** * 待适配 **/ class Dog { public void eat() { System.out.println("吃东西"); } } interface Animal { void eat(); void say(); } /** * 适配后 **/ class AdultDog extends Dog implements Animal { @Override public void say() { System.out.println("汪汪"); } }
-
对象适配器模式
/** * 待适配 **/ class Dog { public void eat() { System.out.println("喝水"); } } interface Animal { void eat(); void say(); } /** * 适配后 **/ class AdultDog implements Animal { private Dog dog; public void setDog(Dog dog) { this.dog = dog; } @Override public void eat() { dog.eat(); } @Override public void say() { System.out.println("汪汪"); } }
装饰器模式
在原有的基础上提供一些新功能称之为装饰器模式。
interface Food {
void eat();
}
class Noodle implements Food {
@Override
public void eat() {
System.out.println("吃面条");
}
}
class Decorator implements Food {
private Food food;
public Decorator(Food food) {
this.food = food;
}
public void eat() {
System.out.println("加热");
food.eat();
}
}
总结 :假如现在有人吃面条嫌面条凉了可以借助装饰者将已有的面条加热后继续吃。
代理模式
将一个类的方法中加入新功能对外提供。代码如下接口和面条类未改变。
class Proxy implements Food {
private Food food;
public Proxy() {
this.food = new Noodle();
}
public void eat() {
System.out.println("加热");
food.eat();
}
}
总结 :代理模式是内部自动创建一份面条,而装饰者是在你已有的面条上进行加热。
代理模式强调控制,装饰者模式强调增强。
两个模式的测试代码。
public static void main(String[] args) {
// 装饰者模式
Food food = new Noodle();
food = new Decorator(food);
food.eat();
// 代理模式
Food proxy = new Proxy();
proxy.eat();
}
外观模式
将内部细节包裹对外只提供简单的流程,例如打开电脑只需要按一下开机键,内部流转无需知道。
class CPU {
void start() {
System.out.println("CPU start");
}
}
class Board {
void start() {
System.out.println("main Board start");
}
}
/**
* 提供外部
*/
class Computer {
private CPU cpu;
private Board board;
public Computer() {
cpu = new CPU();
board = new Board();
}
public void start() {
System.out.println("computer start");
board.start();
cpu.start();
}
}
/**
* 测试代码
*/
public static void main(String[] args) {
Computer computer = new Computer();
computer.start();
}
总结:只需要创建computer类的实例即可打开电脑而不关心CPU和主板如何运行。
桥接模式
将抽象化和实例化相分离。具体实现:
interface Cook {
void cook();
}
class Wang implements Cook {
@Override
public void cook() {
System.out.println("王厨师做饭");
}
}
class Zhang implements Cook {
@Override
public void cook() {
System.out.println("张厨师做饭");
}
}
/**
* 桥接器
*/
class Waiter {
private Cook cook;
public Waiter(Cook cook) {
this.cook = cook;
}
public void serve() {
cook.cook();
}
}
/**
* 测试代码
*/
public static void main(String[] args) {
// 王厨师炒菜
Waiter waiter = new Waiter(new Wang());
waiter.serve();
// 张厨师炒菜
waiter = new Waiter(new Zhang());
waiter.serve();
}
总结: 顾客来饭店吃饭,不关心饭菜是谁炒的,他们只跟服务员接触。由服务员去告知厨师炒什么菜。例如JDBC并不关心你使用的是mysql或者Oracle,你只需要提供对应服务器的接口即可。
组合模式
组合模式又可以叫做部分-整体模式。是用于把一组类似的对象当作一个单一的对象。组合模式依据的是树形结构来组合对象,用来表示“部分或者是整体”的层次。
理解有限无法给出例子。
总结:我的理解就是说 一个文件系统中,用户浏览文件 并不关心浏览的是文件夹还是文件,所以将文件夹还有文件都实现自同一个超类。
享元模式
主要实现对象的共享,具体实现有线程池之类,此处也不给出实例。
行为型模式(11种)
策略模式
将算法中的核心提取出来, 使他们可以相互替换,且算法的变化不会影响到使用算法的客户。
interface Algorithm {
void cal();
}
class Add implements Algorithm {
@Override
public void cal() {
System.out.println("加法运算");
}
}
class Subtraction implements Algorithm {
@Override
public void cal() {
System.out.println("减法运算");
}
}
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
Algorithm alg = new Add();
alg.cal();
alg = new Subtraction();
alg.cal();
}
总结 :设计了一个加法和减法都实现了算法的接口,当创建时创建不同的算法即可实现不同的功能。与桥接模式区别是桥接模式将事情做的更加完善了,例如策略模式是使用不同的方式做一件事情,而桥接模式是在不同的场景下做一件事。
模板方法模式
一个抽象类中,有一个主方法,再定义1…n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用 。
abstract class Algorithm {
abstract void cal();
public void calNums() {
System.out.println("获取两个数");
cal();
System.out.println("返回结果");
}
}
class Add extends Algorithm {
@Override
public void cal() {
System.out.println("加法运算");
}
}
class Subtraction extends Algorithm {
@Override
public void cal() {
System.out.println("减法运算");
}
}
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
Algorithm alg = new Add();
alg.calNums();
alg = new Subtraction();
alg.calNums();
}
总结:模板方法和策略模式其实大体上是相似的,模板方法是将一件事的共同部分留下只将不同的构成一个抽象方法留给子类去扩展,策略模式则是将算法整体去替换。模板模式体现的是将核心的事情留给子类去实现,而策略模式是将整体事情抽离出来方便替换。
观察者模式
当一件事情的状态发生改变时,所有观察这件事的类都将收到通知。
/**
* 被观察对象
*/
class User {
private boolean tag = false;
private List<Observer> list = new ArrayList<>();
public void addObserver(Observer observer) {
list.add(observer);
}
public void removeObserver(Observer observer) {
list.remove(observer);
}
public void editTag() {
for (Observer item : list) {
item.notifyTag();
}
tag = !tag;
}
}
/**
* 观察者
*/
class Observer {
private int index;
public Observer(int index) {
this.index = index;
}
public void notifyTag() {
System.out.println(index + "接到通知");
}
}
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
Observer ob1 = new Observer(1);
Observer ob2 = new Observer(2);
User user = new User();
user.addObserver(ob1);
user.addObserver(ob2);
user.editTag();
}
总结:当User改变时会通知所有观察该类的对象,调用notifyTag()方法使其接受通知。
迭代模式
迭代器模式就是顺序访问聚集中的对象,此处不贴代码展示了,例如ArrayList中的内部类Itr就是一个很好的例子。迭代模式就是相当于一个数组中的指针访问数组对象。
责任链模式
就是用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会(条件匹配)处理请求事务,如果某个节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。
/**
* 处理者
*/
class User {
private int role;
private User next;
public User() {
}
public User(int role, User next) {
this.role = role;
this.next = next;
}
public void handle(int level) {
if (level == role) {
System.out.println("等级" + role + "解决");
} else if (next != null) {
next.handle(level);
} else {
System.out.println("无法解决");
}
}
}
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
User user = new User(1, new User(2, null));
user.handle(2);
}
总结:当第一个人权限不足时会自动调用链上的下一个人去处理,直到处理完成或者都无法解决才返回。
命令模式
命令模式关注动作本身,通过将动作封装成对象实现调用者和底层实现相分离。调用者只需要简单的下达命令,然后等待命令完成即可。
/**
* 执行者
*/
class Light {
void open() {
System.out.println("开灯");
}
void close() {
System.out.println("关灯");
}
}
/**
* 执行者
*/
class TV {
void open() {
System.out.println("打开电视");
}
void close() {
System.out.println("关闭电视");
}
}
/**
* 命令
*/
interface Command {
void open();
void close();
}
/**
* 命令
*/
class LightCommand implements Command {
private Light light;
public LightCommand(Light light) {
this.light = light;
}
@Override
public void open() {
light.open();
}
@Override
public void close() {
light.close();
}
}
/**
* 命令
*/
class TVCommand implements Command {
private TV tv;
public TVCommand(TV tv) {
this.tv = tv;
}
@Override
public void open() {
tv.open();
}
@Override
public void close() {
tv.close();
}
}
/**
* 发布命令
*/
class Control {
private List<Command> list;
public Control() {
this.list = new ArrayList<>();
}
public int add(Command command) {
int size = list.size();
list.add(command);
return size;
}
public void open(int index) {
list.get(index).open();
}
public void close(int index) {
list.get(index).close();
}
}
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
Light light = new Light();
TV tv = new TV();
LightCommand lightCommand = new LightCommand(light);
TVCommand tvCommand = new TVCommand(tv);
Control control = new Control();
int l = control.add(lightCommand);
int t = control.add(tvCommand);
control.open(l);
control.open(t);
control.close(l);
}
总结 : Control类相当于家电遥控器,当按下指定遥控开关时,对应电器打开中间借助了命令类Command,具体实现类为Light和TV类,如果要添加新家电,只需要创建家电本身类和一个命令类即可。
可用于功能相同但具体实现不同,例如所有家电都包含打开和关闭。适用于命令模式。
状态模式
允许一个对象在其内部状态改变时改变它的行为 。
/**
* 状态类
*/
abstract class UserState {
private User user;
public UserState(User user) {
this.user = user;
}
public User getUser() {
return user;
}
abstract void eat();
}
/**
* 打开状态
*/
class OpenUserState extends UserState {
public OpenUserState(User user) {
super(user);
}
@Override
void eat() {
System.out.println("吃东西");
// 吃过之后应该把嘴巴闭上
getUser().setCurrentState(User.CLOSED_STATE);
System.out.println("闭嘴");
}
}
/**
* 嘴巴关闭状态
*/
class CloseUserState extends UserState {
public CloseUserState(User user) {
super(user);
}
@Override
void eat() {
// 闭嘴状态下只能将嘴打开吃东西
getUser().setCurrentState(User.OPENED_STATE);
System.out.println("嘴巴打开");
}
}
/**
* 具体人
*/
class User {
public static UserState OPENED_STATE;
public static UserState CLOSED_STATE;
/**
* 当前状态
*/
private UserState currentState;
/**
* 初始状态是闭嘴
*/
public User() {
OPENED_STATE = new OpenUserState(this);
CLOSED_STATE = new CloseUserState(this);
this.currentState = CLOSED_STATE;
}
public void setCurrentState(UserState currentState) {
this.currentState = currentState;
}
void eat() {
currentState.eat();
}
}
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
User user =new User();
user.eat();
user.eat();
}
/**
* 运行结果
* 嘴巴打开
* 吃东西
* 闭嘴
*/
总结: 人一共有两个状态嘴巴打开和嘴巴关闭,当嘴巴打开时吃方法是可以吃东西的且吃完后状态会变更为关闭,嘴巴关闭时调用吃方法会将嘴巴打开。对应于不同状态,同一个方法执行不同逻辑。相较于多个if else if此模式如果状态新增时,只需要创建新的状态类即可。适合于多个状态多种情况操作,不用在一个方法里判断过多逻辑。
备忘录模式
将一个类创建一个备份方便意外时恢复。
/**
* 原始类
*/
class Original {
private int value;
public void setValue(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public Original(int value) {
this.value = value;
}
/**
* 备份
*
* @return
*/
public Memento createMemento() {
return new Memento(value);
}
/**
* 恢复方法
*
* @param memento
*/
public void restoreMemento(Memento memento) {
this.value = memento.getValue();
}
}
class Memento {
private int value;
public Memento(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
/**
* 存储备忘录
*/
class Storage {
private Memento memento;
public Storage(Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
// 原始
Original original = new Original(10);
// 备份
Storage storage = new Storage(original.createMemento());
original.setValue(12);
System.out.println(original.getValue());
// 恢复备份
original.restoreMemento(storage.getMemento());
System.out.println(original.getValue());
}
总结:Original类在修改之前做一次备份可在修改后提供恢复,Storage是备份管理者,Memento类是实际存储备份类。
访问者模式
访问者模式是一种将数据操作和数据结构分离的设计模式,对于不同的数据结构提供新操作。
interface Visitor {
void visit(Student student);
}
class TeacherVisitor implements Visitor {
@Override
public void visit(Student student) {
System.out.print(student.name);
if (student instanceof StudentA) {
System.out.println(((StudentA) student).getGPA());
} else
System.out.println();
}
}
abstract class Student {
public String name;
public int score;
public Student(String name) {
this.name = name;
score = new Random().nextInt(100);
}
// 核心方法,接受Visitor的访问
public abstract void accept(Visitor visitor);
}
/**
* GPA访问
*/
class StudentA extends Student {
public StudentA(String name) {
super(name);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String getGPA() {
if (this.score > 90)
return "4.0";
else
return "普通";
}
}
/**
* 无GPA访问
*/
class StudentB extends Student {
public StudentB(String name) {
super(name);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
StudentA a = new StudentA("zz");
StudentB b = new StudentB("zz2");
TeacherVisitor visitor = new TeacherVisitor();
a.accept(visitor);
b.accept(visitor);
}
// 输出
// zz普通
// zz2
总结:StudentA和StudentB相当于两种不同的数据结构,StudentA可以将成绩转化为绩点,两者都通过Visitor访问。访问者针对于不同数据结构的相同操作有不同的操作方式。
中介者模式
用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。降低类与类之间的耦合。用于协调多个类相互工作。
例如:我打开A开关就想让B开关也打开,关闭B开关想让A开关同时关闭。
abstract class Switch {
public abstract void open(String info, Mediator mediator);
public abstract void close(String info, Mediator mediator);
}
class SwitchA extends Switch {
@Override
public void open(String info, Mediator mediator) {
System.out.println("A" + info);
mediator.open(info);
}
@Override
public void close(String info, Mediator mediator) {
System.out.println("A" + info);
}
}
class SwitchB extends Switch {
@Override
public void open(String info, Mediator mediator) {
System.out.println("B" + info);
}
@Override
public void close(String info, Mediator mediator) {
System.out.println("B" + info);
mediator.close(info);
}
}
/**
* 中介者
*/
class Mediator {
private Switch aSwitch;
private Switch bSwitch;
public Mediator(Switch aSwitch, Switch bSwitch) {
this.aSwitch = aSwitch;
this.bSwitch = bSwitch;
}
public void open(String info) {
bSwitch.open(info, this);
}
public void close(String info) {
aSwitch.close(info, this);
}
}
/**
* 测试代码
*
* @param args
*/
public static void main(String[] args) {
Switch a = new SwitchA();
Switch b = new SwitchB();
Mediator md = new Mediator(a, b);
a.open("打开", md);
b.close("关闭", md);
}
// 运行结果
// A打开
// B打开
// B关闭
// A关闭
总结:SwitchA和SwitchB是有关联的两个类,通过中介者Mediator使得a和b解耦,关联由中介者管理,如果以后添加新联系只需要更改中介者类即可。
解释器模式
具体笔者也懵懵懂懂就不写出来误导大家了。
参考博客:https://blog.csdn.net/zhangerqing/article/details/8194653 这个系列
笔者理解有限,如果有错误的地方还望指正共同学习。