什么是设计模式
设计模式,是软件开发过程中所面临问题的一般性解决方案,是众多开发人员在很长一段时间中的试验和错误中总结出来的最佳实践。
合理地使用设计模式可增强代码的可靠性和可重用性,同时也使得代码更容易被他人理解。
总的来说,设计模式可分为三大类:
- 创建型 (Creation Patterns):提供一种 “隐藏了具体创建细节” 的对象创建方式。
- 结构型 (Structual Patterns):聚焦于类和对象的组合。
- 行为型 (Behavioral Patterns):聚焦于对象之间的通信。
创建型模式
单例模式
简介
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
优点
公共且不变的资源有且只会有一个对外的实例,避免不必要的内存占用。
缺点
- 相应逻辑都写在一个对象里,很容易违背单一原则。
- 不能扩展,违背开闭原则。
例子
懒汉式单例
当第一次调用 getlnstance 方法时才去创建这个单例。(例子是加了双锁的,但理论上也算是饿汉式)
多线程每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。
// 加了双锁保证线程安全,单线程环境是可以去除的
public class LazySingleton {
private volatile static LazySingleton instance;
private LazySingleton (){}
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
饿汉式单例
在初始化对象时就创建单例实例。
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
工厂方法
简介
顾名思义,就是创建一个工厂来新建对象。一般有四种角色:
- 抽象对象:定义对象的抽象。
- 具体对象:实现抽象对象。
- 抽象工厂:定义工厂的抽象,定义创建对象的抽象方法。
- 具体工厂:实现抽象工厂的抽象方法。
优点
- 用户只需要知道具体工厂的名称就可得到所要的对象,无须知道对象的具体创建过程。
- 灵活性增强,对于新类型对象的创建,只需多写一个相应的工厂类。
- 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点
- 对象每新增一个类型,都需要新增一个具体工厂,类的数量会很多。
- 增加了系统的抽象性和理解难度。
例子
就拿奶茶店为例子,有珍珠奶茶跟水果奶茶,所以四种对象分别是:
- 抽象奶茶:AbstractNaicha
- 具体奶茶(珍珠奶茶、水果奶茶):ZhenzhuNaicha、ShuiguoNaicha
- 奶茶店抽象工厂:AbstractNaichadianFactory
- 奶茶店具体工厂(珍珠奶茶店工厂、水果奶茶店工厂):ZhenzhuNaichadianFactory、ShuiguoNaichadianFactory
// 抽象奶茶对象
interface AbstractNaicha{}
// 珍珠奶茶
public class ZhenzhuNaicha implements AbstractNaicha {
}
// 水果奶茶
public class ShuiguoNaicha implements AbstractNaicha {
}
// 奶茶店抽象工厂
interface AbstractNaichadianFactory {
public AbstractNaicha newNaicha();
}
// 珍珠奶茶店工厂,创建珍珠奶茶
class ZhenzhuNaichadianFactory implements AbstractNaichadianFactory {
public AbstractNaicha newNaicha() {
return new ZhenzhuNaicha();
}
}
// 水果奶茶店工厂,创建水果奶茶
class ShuiguoNaichadianFactory implements AbstractNaichadianFactory {
public AbstractNaicha newNaicha() {
return new ShuiguoNaicha();
}
}
public static void main(String[] args) {
// 先创建一个珍珠奶茶店工厂,再通过工厂创建一个珍珠奶茶
AbstractNaichadianFactory aF = new ZhenzhuNaichadianFactory();
AbstractNaicha zz = aF.newNaicha();
// 先创建一个水果奶茶店工厂,再通过工厂创建一个水果奶茶
AbstractNaichadianFactory bF = new ShuiguoNaichadianFactory();
AbstractNaicha sg = bF.newNaicha();
}
抽象工厂
简介
跟工厂方法模式相比,无需每种对象都有一个具体工厂,因为抽象工厂中一个抽象工厂可包含多种同系列但是不同类型的创建对象的方法,依然是四种角色:
- 抽象对象:定义对象的抽象。
- 具体对象:实现抽象对象。
- 抽象工厂:定义工厂的抽象,定义多种同系列但是不同类型创建对象的抽象方法。
- 具体工厂:实现抽象工厂的抽象方法。
优点
与工厂方法模式一致。
缺点
与工厂方法模式一致。
例子
就拿奶茶店做例子,一个奶茶店工厂,可生产珍珠奶茶与水果奶茶,所以四种角色分别是:
- 抽象奶茶:AbstractNaicha
- 具体奶茶(珍珠奶茶、水果奶茶):ZhenzhuNaicha、ShuiguoNaicha
- 奶茶店抽象工厂:AbstractNaichadianFactory
- 奶茶店具体工厂:NaichadianFactory
// 抽象奶茶对象
interface AbstractNaicha{}
// 珍珠奶茶
public class ZhenzhuNaicha implements AbstractNaicha {
}
// 水果奶茶
public class ShuiguoNaicha implements AbstractNaicha {
}
// 奶茶店抽象工厂
interface AbstractNaichadianFactory {
public AbstractNaicha newZhenzhuNaicha();
public AbstractNaicha newShuiguoNaicha();
}
// 奶茶店工厂,创建珍珠奶茶、水果奶茶
class NaichadianFactory implements AbstractNaichadianFactory {
public AbstractNaicha newZhenzhuNaicha() {
return new ZhenzhuNaicha();
}
public AbstractNaicha newShuiguoNaicha() {
return new ShuiguoNaicha();
}
}
public static void main(String[] args) {
// 创建一个奶茶店工厂
AbstractNaichadianFactory aF = new ZhenzhuNaichadianFactory();
// 通过工厂新建珍珠奶茶
AbstractNaicha a = aF.newZhenzhuNaicha();
// 通过工厂新建水果奶茶
AbstractNaicha b = aF.newShuiguoNaicha();
}
建造者模式
简介
将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。这个模式最重要的是建造者对象,它是由四个部分组成:
- 具体对象(Object):它是包含多个组成部件的具体对象,由具体建造者来创建其各个零部件。
- 抽象建造者(AbstractBuilder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
- 具体建造者(Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
- 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
优点
- 封装性好,构建和表示分离。
- 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
缺点
对象的属性如果发生变化,建造者也要变化,维护成本加大。
例子
以奶茶为例子,一杯纯奶茶,你可以选择加珍珠,加椰果,打包带走等等。 那它的四个组成部分为:
- 具体奶茶:Naicha
- 抽象奶茶建造者:AbstractNaichaBuilder
- 具体奶茶建造者:NaichaBuilder
- 奶茶指挥者:NaichaBuilderDirector
class Naicha {
private boolean zhenzhu;
private boolean yegua;
private boolean dabao;
public void setZhenzhu(boolean isAdd) {
this.zhenzhu = isAdd;
}
public void setYegua(boolean isAdd) {
this.yegua = isAdd;
}
public void setDabao(boolean isAdd) {
this.dabao = isAdd;
}
}
abstract class AbstractNaichaBuilder {
protected Naicha naicha = new Naicha();
public abstract void buildZhenzhu(boolean isAdd);
public abstract void buildYegua(boolean isAdd);
public abstract void buildDabao(boolean isAdd);
//返回奶茶对象
public Naicha build() {
return naicha;
}
}
public class NaichaBuilder extends AbstractNaichaBuilder {
public void buildZhenzhu(boolean isAdd) {
product.setZhenzhu(isAdd);
}
public void buildYegua(boolean isAdd) {
product.setYegua(isAdd);
}
public void buildDabao(boolean isAdd) {
product.setDabao(isAdd);
}
}
class NaichaBuilderDirector {
private AbstractNaichaBuilder builder;
public Director(AbstractNaichaBuilder builder) {
this.builder = builder;
}
// 是否加珍珠
public AbstractNaichaBuilder zhenzhu(boolean isAdd) {
builder.buildZhenzhu(isAdd);
return builder;
}
// 是否加椰果
public AbstractNaichaBuilder yeguo(boolean isAdd) {
builder.buildYegua(isAdd);
return builder;
}
// 是否打包
public AbstractNaichaBuilder dabao(boolean isAdd) {
builder.buildDabao(isAdd);
return builder;
}
}
public static void main(String[] args) {
// 新建构造者
AbstractNaichaBuilder builder = new NaichaBuilder();
// 新建指挥者
NaichaBuilderDirector director = new NaichaBuilderDirector(builder);
// 新建奶茶对象
Naicha naicha = director
.zhenzhu(true)
.yeguo(true)
.dabao(true)
.build();
}
原型模式
简介
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。其中跟这个模式相关的有2个概念:
- 浅拷贝:只是增加了一个指针指向原型对象的内存地址,仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。
- 深拷贝:增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,并把原型对象的属性全部拷贝过来。
java有Cloneable来实现浅拷贝。
行为型模式
模板方法
简介
定义一个操作中的方法骨架,而将方法的一些步骤延迟到子类中,使得子类可以不改变该方法的逻辑结构的情况下重定义某些特定步骤,它分三部分组成:
- 模板方法:定义了方法骨架,骨架内的方法按顺序来执行,一般包含抽象方法、具体方法。
- 抽象方法:对外扩展的方法,由子类实现。
- 具体方法:由父类实现,不允许子类修改。
优点
- 父类封装了不变部分方法,并对子类扩展可变部分方法。
- 符合开闭原则。
缺点
- 扩展方法的每种实现都需要定义一个子类,整个类群会很大。
- 父类新增抽象方法的话,全部子类都需要新增实现。
例子
用户去奶茶店买奶茶,步骤分别是排队、点餐、叫号,其中点餐是可扩展给用户去自定义的,所以三部分为:
- 模板方法:买奶茶。
- 抽象方法:点餐。
- 具体方法:排队,叫号。
public abstract class NaichaDian {
// 买奶茶的入口
public void maiNaicha() {
paidui();
diancan();
jiaohao();
}
// 排队
private void paidui() {
// 由奶茶店实现排队规则
}
// 点餐,用户想吃啥自己决定
public abstract void diancan();
// 叫号
private void paidui() {
// 由奶茶店实现叫号规则
}
}
public class User1 extends NaichaDian {
@Override
public void diancan(){
// 我要喝珍珠奶茶
}
}
public class User2 extends NaichaDian {
@Override
public void diancan(){
// 我要喝水果奶茶
}
}
public static void main(String[] args) {
User1 u1 = new User1();
// 用户1去买一杯珍珠奶茶
u1.maiNaicha();
User1 u2 = new User1();
// 用户2去买一杯水果奶茶
u2.maiNaicha();
}
中介者模式
简介
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,迪米特法则的典型应用。它由四个角色组成:
- 抽象中介者(AbstractMediator):它是中介者的接口,提供了实例对象之间的交互功能的抽象方法。
- 具体中介者(Mediator)角色:实现中介者接口,定义一个 List 来管理抽象对象,协调各个实例对象之间的交互关系,因此它依赖于抽象对象类。
- 抽象对象类(AbstractObject):定义实例对象类的接口,保存中介者对象,提供实例对象交互的抽象方法,实现所有相互影响的实例对象类的公共功能。
- 具体对象类(Object):是抽象对象类的实现者,当需要与其他实例对象交互时,由中介者对象负责后续的交互。
优点
降低对象之间的耦合性,符合迪米特原则。
缺点
未有明显缺点。
例子
最贴近的就是网络聊天室的例子,小A跟小B都在聊天室注册自己的信息,然后发消息跟接消息都通过聊天室转发,于是四个角色为:
- 抽象中介者:AbstractChatMediator
- 具体中介者** **:ChatMediator。
- 抽象对象类:AbstractUser。
- 具体对象类:UserA、UserB。
public abstract class AbstractChatMediator {
// 注册用户
public abstract void register(User user);
// 转发消息
public abstract void relay(User user, String msg);
}
public class ChatMediator {
private Map<Integer, User> users = new HashMap<>();
// 注册用户到自己的用户列表里
public void register(User user) {
users.put(user.id, user);
}
// 转发给目标用户
public void relay(Integer id, String msg) {
// 这里默认该用户一定在注册到列表里
users.get(id).receive(id, msg)
}
}
public abstract class User {
// 用户必须依赖中介
protected AbstractChatMediator mediator;
public void setMediator(AbstractChatMediator mediator) {
this.mediator = mediator;
}
// 接收指定id的消息
public abstract void receive(Integer id, String msg);
// 发送指定id的消息
public abstract void send(Integer id, String msg);
}
public class UserA extends User {
public Integer id = 1;
// 接收指定id的消息
public void receive(Integer id, String msg){
// 怎么处理UserA自己决定。
}
// 发送指定id的消息
public void send(Integer id, String msg){
// 让中介者转发
mediator.relay(id, msg);
}
}
public class UserB extends User {
public Integer id = 2;
// 接收指定id的消息
public void receive(Integer id, String msg){
// 怎么处理UserB自己决定。
}
// 发送指定id的消息
public void send(Integer id, String msg){
// 让中介者转发
mediator.relay(id, msg);
}
}
public static void main(String[] args) {
User ua = new UserA();
User ub = new UserB();
// a 向 b 发消息
ua.send(2, "hello");
// b 向 a 发消息
ub.send(1, "hello");
}
命令模式
简介
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。它由四种角色组成:
- 抽象命令类(AbstractCommand):声明执行命令的接口,一般拥有执行命令的抽象方法 execute()。
- 具体命令类(Command):是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
- 实现者/接收者(Receiver):执行命令功能的相关操作,是具体命令对象业务的真正实现者。
- 调用者/请求者(Invoker):是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
优点
扩展性良好,增加或删除命令非常方便,并且不会影响其他类,符合开闭原则。
缺点
可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性。
例子
奶茶店买喝的,用户打开小程序,点了一份珍珠奶茶然后下单,这时候“制作珍珠奶茶”的命令就传给了奶茶店,后来用户觉得不够喝,又点了一份水果奶茶然后下单,“制作水果奶茶”的命令传给了奶茶店,后来发现吃太多会胖的,于是又取消了水果奶茶的订单,“取消水果奶茶”的命令传给了奶茶店。
- 抽象命令类:AbstractNaichaCommand
- 具体命令类:MakeZhenzhuNaichaCommand制作珍珠奶茶、MakeShuiguoNaichaCommand制作水果奶茶、CancelShuiguoNaichaCommand取消水果奶茶
- 实现者/接收者:NaichadianReceiver
- 调用者/请求者:UserInvoker
// 奶茶店接收者
class NaichadianReceiver {
// 开始制作奶茶
public void make(String name) {
}
// 停止制作奶茶
public void stop(String name) {
}
}
// 抽象奶茶命令
interface AbstractNaichaCommand {
public void execute();
}
// 制作珍珠奶茶命令
class MakeZhenzhuNaichaCommand implements AbstractNaichaCommand {
private NaichadianReceiver receiver;
public ZhenzhuNaichaCommand(NaichadianReceiver receiver) {
this.receiver = receiver;
}
// 把制作珍珠奶茶命令给奶茶店
@Override
public void execute() {
receiver.make("珍珠");
}
}
// 制作水果奶茶命令
class MakeShuiguoNaichaCommand implements AbstractNaichaCommand {
private NaichadianReceiver receiver;
public ShuiguoNaichaCommand(NaichadianReceiver receiver) {
this.receiver = receiver;
}
// 把制作水果奶茶命令给奶茶店
@Override
public void execute() {
receiver.make("水果");
}
}
// 取消制作水果奶茶命令
class CancelShuiguoNaichaCommand implements AbstractNaichaCommand {
private NaichadianReceiver receiver;
public ShuiguoNaichaCommand(NaichadianReceiver receiver) {
this.receiver = receiver;
}
// 把制作水果奶茶命令给奶茶店
@Override
public void execute() {
receiver.cancel("水果");
}
}
// 用户请求者
class UserInvoker {
// 下单、取消订单、评论....
public void action(AbstractNaichaCommand command) {
command.execute();
}
}
public static void main(String[] args) {
// 先创建一个奶茶店接收者
NaichadianReceiver receiver = new NaichadianReceiver();
// 再创建一个用户请求者
UserInvoker invoker = new Invoker();
// 下单珍珠奶茶
invoker.action(new MakeZhenzhuNaichaCommand(receiver));
// 下单水果奶茶
invoker.action(new MakeShuiguoNaichaCommand(receiver));
// 取消水果奶茶
invoker.action(new CancelShuiguoNaichaCommand(receiver));
}
责任链模式
简介
为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。主要包含以下角色:
- 抽象处理者(AbstractHandler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
- 具体处理者(Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
- 客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
优点
- 降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
- 增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
缺点
- 不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
- 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
例子
小红请假会经过层层管理者的审批,比如小红-部门经理-区域经理-老板。请三天以下,部门经理可以直接同意,五天以下区域经理可以直接同意,请三十天以下老板直接同意,但是大于三十天就直接拒绝。这三种角色分别为:
- 抽象处理者:AbstractHandler
- 具体处理者:BumenJingliHandler、QuyuJingliHandler、BossHandler
- 客户类:XiaohongClient
public abstract AbstractHandler {
private AbstractHandler nextHandler;
public void setNextHandler(AbstractHandler handler){
this.nextHandler = handler;
}
public AbstractHandler getNextHandler() {
return nextHandler;
}
// 处理请假
public abstract Boolean handlerQingjia(int day);
}
public class BumenJingliHandler extends AbstractHandler {
// 不小于三天,可以直接同意,否则转到下一级
@Override
public Boolean handlerQingjia(int day) {
if (day <= 3) {
return true
} else {
return getNextHandler().handlerQingjia(day);
}
}
}
public class QuyuJingliHandler extends AbstractHandler {
// 不小于五天,可以直接同意,否则转到下一级
@Override
public Boolean handlerQingjia(int day) {
if (day <= 5) {
return true
} else {
return getNextHandler().handlerQingjia(day);
}
}
}
public class BossHandler extends AbstractHandler {
// 不小于三十天,可以直接同意,否则拒绝
@Override
public Boolean handlerQingjia(int day) {
if (day <= 30) {
return true
} else {
return false;
}
}
}
// 小红
public class XiaohongClient {
private AbstractHandler handle;
public Xiaohong(AbstractHandler handle) {
this.handle = handle;
}
// 请假会先给直接关联的处理者
public Boolean handlerQingjia(int day) {
return handle.handlerQingjia(day);
}
}
public static void main(String[] args) {
BossHandler boss = new BossHandler();
QuyuJingliHandler quyuJingli = new QuyuJingliHandler();
BumenJingliHandler bumenJingli = new BumenJingliHandler();
// 小红跟部门经理直接关联
XiaohongClient xiaohong = new XiaohongClient(bumenJingli);
// 部门经理的下级是区域经理
bumenJingli.setNextHandler(quyuJingli);
// 区域经理的下级是老板
bumenJingli.setNextHandler(boss);
// 小红开始请假4天,不出意外部门经理就可以同意
xiaohong.handlerQingjia(4);
}
策略模式
简介
该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。策略模式的主要角色如下:
- 抽象策略(AbstractStrategy):定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略(Strategy):实现了抽象策略定义的接口,提供具体的算法实现。
- 环境(Context):持有一个策略类的引用,最终给客户端调用。
优点
策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法。
缺点
- 客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类。
- 策略模式造成很多的策略类,增加维护难度。
例子
出去旅游,可以坐火车、坐高铁、搭飞机到达旅游目的地。这三种角色分别是:
- 抽象策略:AbstractStrategy(抽象行程方式策略)
- 具体策略:HuocheStrategy、GaotieStrategy、FeijiStrategy
- 环境:Context
interface AbstractStrategy {
public void execute();
}
public class HuocheStrategy implements AbstractStrategy {
@Override
public void execute() {
// 买火车票,准备行李,去火车站,上车
}
}
public class GaotieStrategy extends AbstractStrategy {
@Override
public void execute() {
// 买高铁票,准备行李,去高铁站,上车
}
}
public class FeijiStrategy extends AbstractStrategy {
@Override
public void execute() {
// 买飞机票,准备行李,去飞机站,上飞机
}
}
public class Context {
private AbstractStrategy strategy;
public Context(AbstractStrategy strategy) {
this.strategy = strategy;
}
// 决定什么行程工具策略
public void execute() {
strategy.execute();
}
}
public static void main(String[] args) {
// 还是坐高铁吧
Context context = new Context(new GaotieStrategy());
context.execute();
}
迭代器模式
简介
提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。java中集合提供了Iterator这种迭代类,比较容易理解,所以不去过多解说。
观察者模式
简介
指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,观察者模式的主要角色如下:
- 抽象主题(AbstractSubject):也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Subject):也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- 抽象观察者(AbstractObserver):它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Observer):实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
优点
降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
缺点
当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
例子
小红、小绿在微信关注了Xx公众号,当Xx公众号有更新时,就会通知关注的用户小红、小绿。这四种角色为:
- 抽象主题:AbstractGongzhonghaoSubject。
- 具体主题:XxGongzhonghaoSubject。
- 抽象观察者:AbstractObserver 用户观察者。
- 具体观察者:XiaohongObserver、XiaolvObserver。
interface AbstractObserver {
// 接收主题的通知并进行处理
void receive();
}
public class XiaohongObserver implements AbstractObserver {
@Override
public void receive() {
// 小红发现公众号更新了,赶紧去看一波
}
}
public class XiaolvObserver implements AbstractObserver {
@Override
public void receive() {
// 小绿发现公众号更新了,不鸟它
}
}
interface AbstractGongzhonghaoSubject {
// 关注
void guanzhu(AbstractObserver observer);
// 取消关注
void quxiaoGuanzhu(AbstractObserver observer);
// 更新内容
void gengxin();
}
public class XxGongzhonghaoSubject implements AbstractGongzhonghaoSubject {
// 关注者列表
List<AbstractObserver> observers = new ArrayList<>();
// 关注
@Override
public void guanzhu(AbstractObserver observer) {
observers.add(observer);
}
// 取消关注
@Override
public void quxiaoGuanzhu(AbstractObserver observer) {
observers.remove(observer);
}
// 更新内容,通知关注者们
@Override
public void gengxin(){
for(AbstractObserver observer: observers) {
observer.receive();
}
}
}
public static void main(String[] args) {
XiaohongObserver xiaohong = new XiaohongObserver();
XiaolvObserver xiaolv = new XiaolvObserver();
AbstractGongzhonghaoSubject xxSubject = new XxGongzhonghaoSubject();
// 小红小绿先关注一波Xx公众号(这里例子不太好,就好像是公众号关注了他俩,但能理解就行)
xxSubject.guanzhu(xiaohong);
xxSubject.guanzhu(xiaolv);
// xx公众号开始更新内容,通知他们
xxSubject.gengxin();
}
状态模式
简介
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。状态模式包含以下主要角色:
- 环境类(Context):也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态对象的切换。
- 抽象状态(AbstractState):定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为。
- 具体状态(State):实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
优点
结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
缺点
- 状态越多,对象越多。
- 状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
例子
请假,那请假状态就可以是未申请、已申请未审批、已审批三种状态,所以三种角色分别为:
- 环境类:QingjiaContext。
- 抽象状态:AbstractQingjiaState
- 具体状态:NotShenqingState未申请、NotShenpiState未审批、YiShenpiState已审批通过。
public abstract class AbstractQingjiaState {
private QingjiaContext context;
public void setContext(QingjiaContext context) {
this.context = context;
}
// 申请请假
public abstract bool shenqing();
// 审批申请
public abstract bool shenpi();
}
// 还没申请
public class NotShenqingState extends AbstractQingjiaState {
// 申请请假,并把上下文的状态改成下一个状态
public bool shenqing(){
this.setContext(new NotShenpiState());
return true;
}
// 还没申请,当然没到审批那一步
public bool shenpi(){
return false;
}
}
// 还没审批
public class NotShenpiState extends AbstractQingjiaState {
// 已经申请,不需要再次申请
public bool shenqing() {
return false;
}
// 审批申请,并把上下文的状态改成下一个状态
public bool shenpi(){
this.setContext(new YiShenpiState());
return true;
}
}
// 已审批,属于最终状态了,所以对于的行为都是返回false,不成功
public class YiShenpiState extends AbstractQingjiaState {
public bool shenqing() {
return false;
}
public bool shenpi() {
return false;
}
}
public class QingjiaContext {
private AbstractQingjiaState state;
// 初始化请假上下文时,请假的状态还是未申请。
public QingjiaContext() {
this.state = new NotShenqingState();
}
public void setState(AbstractQingjiaState state) {
this.state = state
}
public void getState() {
return state
}
// 申请请假
public bool shenqing(){
return this.state.shenqing();
}
// 审批申请
public bool shenpi(){
return this.state.tongguo();
}
}
public static void main(String[] args) {
// 可以设定两种场景
// 正常的流程,申请,审批,获取请假状态,最终的状态肯定是已审批
QingjiaContext c1 = new QingjiaContext();
c1.shenqing();
c1.shenpi();
c1.getState();
// 不正常的流程,直接审批,然后获取请假状态,最终的状态是未申请
QingjiaContext c1 = new QingjiaContext();
c1.shenpi();
c1.getState();
// 其实每种请求行为都会返回布尔值,可根据这个布尔值判断当前的请假行为流程是否有效。
}
备忘录模式
简介
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态,该模式又叫快照模式。模式有三种角色:
- 发起者(Originator):记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
- 备忘录(Memento):负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
- 管理者(Caretaker):对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
优点
提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
缺点
资源消耗大。
例子
游戏的存档机制,就是备忘录模式。游戏提供存档、读档2个功能,存档文件保存游戏进度的状态,存档管理者就管理存档文件。所以三种角色为:
- 发起者:GameOriginator
- 备忘录:CundangMemento
- 管理者:CundangCaretaker
public class CundangMemento {
}
public class CundangCaretaker {
private List<CundangMemento> mementos = new ArrayList<>();
// 获取存档列表
public List<CundangMemento> getMementos() {
return mementos;
}
// 保存存档
public void save(CundangMemento memento) {
mementos.add(memento);
}
// 读档
public CundangMemento dudang(Integer state){
return caretaker.get(state);
}
// 后续可加覆盖档、删档..
}
public class GameOriginator {
private CundangCaretaker caretaker = new CundangCaretaker();
// 获取存档列表
public List<CundangMemento> getMementos() {
return caretaker.getMementos();
}
// 存档
public void save() {
return new CundangMemento();
}
// 读档
public CundangMemento dudang(Integer state){
return caretaker.dudang(state);
}
}
public static void main(String[] args) {
// 创建游戏(自带存档管理器)
GameOriginator originator = new GameOriginator();
// 存档
originator.save();
// 获取存档列表
originator.getMementos();
// 可根据存档列表选择想读取的存档,这里省略获取列表下标的步骤,直接假设一个1来进行读档。
originator.dudang(1);
}
解释器模式
简介
给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。这种模式实现了文法表达式处理的接口,该接口解释一个特定的上下文。模式有以下角色:
- 抽象表达式(Abstract Expression):定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。
- 终结符表达式(Terminal Expression):是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
- 非终结符表达式(Nonterminal Expression):也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
- 环境(Context):通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
- 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。
解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题,所以这里不展开讲,有兴趣的童鞋可自行查阅相关资料。
访问者模式
简介
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。模式有几种角色:
- 抽象访问者(AbstractVisitor):定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。visit方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 AbstractVisitor接口,如果出现这种情况,则说明不适合使用访问者模式。
- 具体访问者(Visitor):实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
- 抽象元素(AbstractElement):声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
- 具体元素(Element):实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
- 对象结构(ObjectStructure):是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
适用场景
通常在以下情况可以考虑使用访问者模式:
- 对象结构相对稳定,但其操作算法经常变化的程序。
- 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
- 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。
优点
- 扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
- 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
- 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
- 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
缺点
- 破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
- 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
例子
奶茶店有2种奶茶,珍珠奶茶、水果奶茶,非会员原价买,而会员则可打9折。所以这几种角色为:
- 抽象访问者:AbstractVisitor
- 具体访问者:NoHuiyuanVisitor非会员访问者、HuiyuanVisitor会员访问者
- 抽象元素:AbstractNaichaElement
- 具体元素:ZhenzhuNaichaElement珍珠奶茶、ShuiguoNaichaElement水果奶茶
- 对象结构:NaichadianStructure奶茶店
interface AbstractVisitor {
// 访问者去买珍珠奶茶的价格
double visit(ZhenzhuNaichaElement elemen);
// 访问者去买水果奶茶的价格
double visit(ShuiguoNaichaElement elemen);
// 后续如果有新的类型奶茶,都在这里加即可
}
// 非会员买各种奶茶的价格都是原价
public class NoHuiyuanVisitor implements AbstractVisitor {
public double visit(ZhenzhuNaichaElement elemen){
return elemen.getPrice();
}
public double visit(ShuiguoNaichaElement elemen){
return elemen.getPrice();
}
}
// 会员买各种奶茶的价格都是打9折
public class NoHuiyuanVisitor implements AbstractVisitor {
public double visit(ZhenzhuNaichaElement elemen){
return elemen.getPrice() * 0.9;
}
public double visit(ShuiguoNaichaElement elemen){
return elemen.getPrice() * 0.9;
}
}
public abstract class AbstractNaichaElement {
private double price;
public AbstractNaichaElement(double price){
this.price=price;
}
public double getPrice() {
return price;
}
abstract double accept(AbstractVisitor visitor);
}
public class ZhenzhuNaichaElement extends AbstractNaichaElement {
public ZhenzhuNaichaElement(double price) {
super(price);
}
@Override
public double accept(AbstractVisitor visitor) {
return visitor.visit(this);
}
}
public class ShuiguoNaichaElement extends AbstractNaichaElement {
public ShuiguoNaichaElement(double price) {
super(price);
}
@Override
public double accept(AbstractVisitor visitor) {
return visitor.visit(this);
}
}
public class NaichadianStructure {
List<AbstractNaichaElement> elements = new ArrayList<>();
// 这里可遍历奶茶对象,通过传入的会员类型来获取全部奶茶的价格
public void accept(AbstractVisitor visitor) {
for(AbstractNaichaElement element: elements) {
// 获取不同的价格
element.accept(visitor);
}
}
public void add(AbstractNaichaElement element) {
elements.add(element);
}
}
public static void main(String[] args) {
// 新建奶茶店
NaichadianStructure naichadian = new NaichadianStructure();
// 新建2种奶茶
naichadian.add(new ZhenzhuNaichaElement(10));
naichadian.add(new ShuiguoNaichaElement(10));
// 非会员查看全部奶品的价格
naichadian.accept(new NoHuiyuanVisitor());
// 会员查看全部奶品的价格
naichadian.accept(new HuiyuanVisitor());
}
结构型模式
适配器模式
简介
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式包含以下主要角色:
- 目标接口(Target):当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee):它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter):它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
优点
- 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
- 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
- 在很多业务场景中符合开闭原则。
缺点
增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
例子
苹果数据线有充电,绑东西的作用,现在华为手机充电,但现在只有苹果数据线、转换器,转换器支持苹果数据线头转安卓数据线头,于是把苹果线头插到转换器里,华为使用转换器就可以通过苹果数据线来充电了,这几个角色为:
- 目标接口:HuaweiTarget
- 适配者():PingguoAdaptee
- 适配器():Pingguo2HuaweiAdapter苹果转华为
interface HuaweiTarget {
void chongdian();
}
public class PingguoAdaptee {
public void chongdian() {
}
// 苹果数据线不单单可充电,有时可以作为一个绳子绑东西用的
public void bangDongxi() {
}
}
public class Pingguo2HuaweiAdapter extends PingguoAdaptee implements HuaweiTarget {
@Override
public void chongdian() {
// 转换器把苹果充电的功能改一下,改成适合安卓的
}
}
public static void main(String[] args) {
Pingguo2HuaweiAdapter adapter = new Pingguo2HuaweiAdapter();
// 给华为充电
adapter.chongdian();
// 绑东西
adapter.bangDongxi();
}
桥接模式
简介
将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。桥接模式包含以下主要角色:
- 抽象化(AbstractObject):定义抽象类,并包含一个对实现化对象的引用。
- 扩展抽象化(RefinedAbstracttObject):是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(AbstractImplementor):定义实现化角色的接口,供扩展抽象化角色调用。
- 具体实现化(Implementor):给出实现化角色接口的具体实现。
适用场景
桥接模式的一个常用使用场景就是为了替换 继承,继承本身具备强侵入性(父类代码侵入子类),造成子类臃肿,因此,优先使用组合/聚合的方式。
桥接模式的特点是将抽象与实现分离,因此它适合
- 一个类存在两个或多个独立变化的维度,而且这两个维度都需要扩展
- 不希望或不适用继承的场景
优点
- 抽象与实现分离,扩展能力强
- 符合开闭原则
缺点
无
例子
奶茶店卖奶茶,按种类分是有珍珠奶茶、水果奶茶,按分量分是有中杯、大杯。,只要在分量里携带种类,就可组合种类跟分量来进行买奶茶,所以四种角色为:
- 抽象化:AbstractFenliang
- 扩展抽象化:ZhongFenliang、DaFenliang
- 实现化:AbstractZhonglei
- 具体实现化:ZhenzhuZhonglei、ShuiguoZhonglei
interface AbstractZhonglei {
void add();
}
public class ZhenzhuZhonglei implements AbstractZhonglei {
public void add(){
// 珍珠奶茶
}
}
public class ShuiguoZhonglei implements AbstractZhonglei {
public void add(){
// 水果奶茶
}
}
public abstract class AbstractFenliang {
protected AbstractZhonglei zhonglei;
public Abstraction(AbstractZhonglei zhonglei) {
this.zhonglei = zhonglei;
}
abstract void add();
}
public class ZhongFenliang AbstractFenliang {
public Abstraction(AbstractZhonglei zhonglei) {
super(zhonglei);
}
public void add(){
// 中杯
// + 种类
zhonglei.add();
}
}
public class DaFenliang AbstractFenliang {
public Abstraction(AbstractZhonglei zhonglei) {
super(zhonglei);
}
public void add(){
// 大杯
// + 种类
zhonglei.add();
}
}
public static void main(String[] args) {
// 大杯珍珠奶茶
new DaFenliang(new ZhenzhuZhonglei());
// 中杯水果奶茶
new ZhongFenliang(new ShuiguoZhonglei());
}
组合模式
简介
时又叫作整体-部分(Part-Whole)模式,它是一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”的关系,使用户对单个对象和组合对象具有一致的访问性。
理论上就是一个树状的数据模式,用的并不多,有兴趣的童学可自行上网查阅。
装饰模式
简介
在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。装饰器模式主要包含以下角色:
- 抽象构件(AbstractComponent):定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(Cmponent):实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(AbstractDecorator):继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(Decorator):实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
优点
- 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
- 装饰器模式完全遵守开闭原则
缺点
无
例子
手抓饼,你可以让老板加鸡蛋、加火腿…,这样的话,四种角色为:
- 抽象构件:AbstractShouzhuabingComponent
- 具体构件:ShouzhuabingComponent
- 抽象装饰:AbstractShouzhuabingDecorator
- 具体装饰:JidanDecorator、HuotuiDecorator
interface AbstractShouzhuabingComponent {
public void add();
}
class ShouzhuabingComponent implements AbstractShouzhuabingComponent {
public void add() {
// 添加一个手抓饼
}
}
class AbstractShouzhuabingDecorator implements AbstractShouzhuabingComponent {
private AbstractShouzhuabingComponent component;
public AbstractShouzhuabingDecorator(AbstractShouzhuabingComponent component) {
this.component = component;
}
public void add() {
component.add();
}
}
// 鸡蛋
class JidanDecorator extends AbstractShouzhuabingDecorator {
public JidanDecorator(AbstractShouzhuabingComponent component) {
super(component);
}
public void add() {
super.add();
// 在别的装饰基础上加个鸡蛋
}
}
// 火腿
class HuotuiDecorator extends AbstractShouzhuabingDecorator {
public HuotuiDecorator(AbstractShouzhuabingComponent component) {
super(component);
}
public void add() {
super.add();
// 在别的装饰基础上加个火腿
}
}
public static void main(String[] args) {
// 先来个手抓饼
ShouzhuabingComponent shouzhuabing = new ShouzhuabingComponent();
// 加个鸡蛋
JidanDecorator jidan = new JidanDecorator(shouzhuabing);
// 加个火腿
HuotuiDecorator huotui = new HuotuiDecorator(jidan);
}
外观模式
简介
又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。三个角色:
- 外观(Facade):为多个子系统对外提供一个共同的接口。
- 子系统(Sub System):实现系统的部分功能,客户可以通过外观角色访问它。
- 客户(Client):通过一个外观角色访问各个子系统的功能。
优点
- 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
缺点
- 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
例子
小时候读小学时,没钱了,直接问妈妈要5毛钱买辣条吃,于是妈妈打开柜子,打开存钱罐,拿出5毛钱,然后给我。对于我来说,只需要知道我问妈妈要钱即可,无需知道妈妈怎么变出钱来的。于是三种角色为:
- 外观:MotherFacade
- 子系统:Dakaiguizi、DakaiCunqianguang、Nachu5
- 客户:MeClient
public class MotherFacade {
private Dakaiguizi dakaiguizi = new Dakaiguizi();
private DakaiCunqianguang dakaiCunqianguang = new DakaiCunqianguang();
private Nachu5 nachu5 = new Nachu5();
// 给钱
public void geiqian() {
// 打开柜子,打开存钱罐,拿出5毛钱
dakaiguizi.do();
dakaiCunqianguang.do();
nachu5.do();
}
}
public class Dakaiguizi {
public void do(){
// 打开柜子
}
}
public class DakaiCunqianguang {
public void do(){
// 打开存钱罐
}
}
public class Nachu5 {
public void do(){
// 拿出5毛钱
}
}
public class MeClient {
// 向妈妈要钱
public void yaoqian(MotherFacade mother){
mother.geiqian();
}
}
public static void main(String[] args) {
MotherFacade mother = new MotherFacade();
MeClient me = new MeClient();
me.yaoqian(mother);
}
享元模式
简介
运用共享技术来有效地支持大量细粒度对象的复用。享元模式的主要角色有如下。
- 抽象享元角色(AbstractFlyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口。
- 具体享元(Flyweight):实现抽象享元角色中所规定的接口。
- 享元工厂(FlyweightFactory):负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
其中可扩展的是,内部状态与外部状态,也就是享元部分与非享元部分。java的线程池就是用了享元模式,非享元部分就是对线程池的配置,享元部分就是复用的线程池。
优点
相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
缺点
读取状态会消耗更多时间。
例子
妈妈在厨房做菜,然后用同一个国产铁锅就可以炒鸡蛋、炒白菜了,当然妈妈还有一个进口铁锅。所以三种角色是:
- 抽象享元角色:AbstractZuocaiFlyweight做菜享元角色
- 具体享元:TieguoFlyweight铁锅享元
- 享元工厂:TieguoFlyweightFactory铁锅享元工厂
// 因为可能扩展为内外状态,所以是抽象类作为父类
public abstract class AbstractZuocaiFlyweight {
abstract void do();
}
public class TieguoFlyweight extends AbstractZuocaiFlyweight {
private String name;
public TieguoFlyweight(String name) {
this.name = name;
}
@Override
public void do(){
// 铁锅开始炒菜
}
}
public class TieguoFlyweightFactory {
Map<String, TieguoFlyweight> tieguos = new HashMap<>();
public TieguoFlyweight getTieguoFlyweight(String name) {
if tieguos.containsKey(name) {
return tieguos.get(name);
} else {
return tieguos.put(name, new TieguoFlyweight())
}
}
}
public static void main(String[] args) {
TieguoFlyweightFactory factory = new TieguoFlyweightFactory();
// 妈妈要拿出国产铁锅炒鸡蛋、炒白菜
factory.getTieguoFlyweight("国产铁锅");
// 觉得味道不好,又拿出进口铁锅炒胡萝卜
factory.getTieguoFlyweight("进口铁锅");
}
代理模式
简介
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。代理模式的主要角色如下:
- 抽象主题(AbstractSubject):通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Subject):实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy):提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
优点
- 代理对象可以扩展目标对象的功能
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性
例子
小明想去法国购买法国香水,但是又不想自己去法国买,于是他找了一个代购,通过他来买到法国香水。所以三种角色为:
- 抽象主题:AbstractFaguoSubject
- 真实主题:FaguoSubject
- 代理:DaigouProxy
interface AbstractFaguoSubject {
void buy(String name);
}
public class FaguoSubject implements AbstractXiangshuiSubject {
public void buy(String name){
// 去法国买什么
}
}
public class DaigouProxy {
private AbstractFaguoSubject faguo;
public DaigouProxy(AbstractFaguoSubject faguo) {
this.faguo = faguo;
}
public void buy(String name) {
this.faguo.buy(name);
}
}
public static void main(String[] args){
// 创建个法国
AbstractFaguoSubject faguo = new FaguoSubject();
// 创建个代购
DaigouProxy daigou = new DaigouProxy(faguo);
daigou.buy("法国香水");
// 以后想去法国买啥都可以通过代购来买。
}