12.结构型设计模式-享元模式
12.1.享元模式简介
(1)简介
- 享元设计模式(Flyweight Pattern):属于结构型设计模式,主要用于减少创建对象的数量,以减少内存占用和提高性能,他提供了减少对象数量从而改善应用所需的对象结构方式。享元模式尝试重试现有的同类对象,如果未找到匹配对象,则创建新对象。
(2)应用场景
- Java中的String,如果字符串常量池里有则返回,如果没有则创建一个字符串保存在字符串常量池中。
- 数据库连接池、线程池等。
- 系统中有大量类似的对象,或者需要用到缓冲池的时候可以用到享元设计模式,池化技术。
- 如果发现某个对象生成了大量细粒度的实例,并且这些实例除了几个参数之外基本都是相同的,如果把这些共享参数移到类外面,在方法调用时将他们传递进来,也可以通过共享对象,减少实例的个数。
(3)内部状态
- 不会随环境的改变而有所不同,是可以共享的。
(4)外部状态
- 不可以共享的,他随环境的改变而改变,因此外部状态是由客户端来支持(因为环境的变化一般是由客户端引起的)。
- (5)角色
- **抽象享元角色:**为具体享元角色规定了必须实现的方法,而外部状态就是一参数的形式通过此方法传入。
- **具体享元角色:**实现抽象角色规定的方法。如果存在内部状态,就负责为内部状态提供存储空间。
- **享元工厂角色:**负责创建和管理享元角色。要想达到共享的目的,这个角色的实现是关键。
- 客户端角色:维护对所有享元对象的引用,而且还需要存储对应的外部状态。
(6)类关系图
- 创建抽象享元角色FlyWeight。
- 创建具体享元角色ConcreteWebSite。
- 创建享元工厂,统一生产。
12.2.享元模式案例实战
(1)编码实战
- 顶层接口CloudWebSite,抽象享元角色
public interface CloudWebSite { void run(Company company); }
- 接口实现类ConcreteWebSite,具体享元角色
public class ConcreteWebSite implements CloudWebSite { private String category; public ConcreteWebSite(String category){ this.category = category; } @Override public void run(Company company) { System.out.println("网站分类:"+category+",公司:"+company.getName()); } }
- 享元模式中具体实体,Company
public class Company { private String name; public Company(String name) { this.name = name; } public Company() { } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Company{" + "name='" + name + '\'' + '}'; } }
- 享元工厂类WebSiteFactory
public class WebSiteFactory { //这快用map结构,模拟享元模式下如果有就直接返回,没有在重新创建 private Map<String,ConcreteWebSite> map = new HashMap<>(); public ConcreteWebSite getWebSiteByKey(String key){ if(map.containsKey(key)){ return map.get(key); }else{ ConcreteWebSite webSite = new ConcreteWebSite(key); map.put(key,webSite); return webSite; } } /** * 获取站点数量 * @return */ public int getWebSiteCategorySize(){ return map.size(); } }
- 测试
WebSiteFactory factory = new WebSiteFactory(); ConcreteWebSite webSite = factory.getWebSiteByKey("公司官网"); webSite.run(new Company("百度")); ConcreteWebSite webSite2 = factory.getWebSiteByKey("公司官网"); webSite2.run(new Company("字节跳动")); ConcreteWebSite webSite3 = factory.getWebSiteByKey("信息流"); webSite3.run(new Company("字节跳动")); System.out.println(factory.getWebSiteCategorySize());
(2)优点和缺点
- 优点
- 大大减少了对象的创建,降低了程序内存的占用,提高效率。
- 缺点
- 提高了系统的复杂度,需要分离出内部状态和外部状态。
- 注意划分内部状态和外部状态,否则可能会引起线程安全的问题,必须有一个工厂类加以控制。
- 享元设计模式和原型、单例模式的区别
- 原型设计模式是指创建对象的种类,然后通过拷贝这些原型来创建新的对象。
- 单例设计模式保证一个类仅有一个实例。
13.行为型设计模式-策略模式
13.1.策略模式简介
(1)简介
- 定义一些列的算法,把他们一个个封装起来,并且是他们可以相互替换。比如天猫双十一,活动有打折的、有满减的、有返利的等等,这些算法只是一种策略,并且是随时都可以进行相互替换。
- (2)应用场景
- 如果在一个系统中有许多类,它们之间的区别仅在于他们的行为,那么可以使用策略模式。
- 不希望暴漏复杂的、与算法相关的业务逻辑,可以使用策略模式,比如支付场景。
- (3)角色
- **Context上下文:**屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
- **Strategy策略角色:**抽象策略角色,是对策略,算法家族的抽象。
- **ConcreteStrategy具体策略角色:**用于实现抽象策略中的操作,即实现具体的算法。
(4)类关系图
- 创建抽象策略接口Stategy。
- 创建具体策略实现类ConcreteStrategy。
- 创建上下文Context类,持有策略实例,传入具体策略实例执行不同的策略。
13.2.策略模式案例实战
(1)编码实战
- 策略抽象类Strategy
public abstract class Strategy { /** * 策略抽象类,用于不同的策略具体实现计算价格 * @param productOrder * @return */ public abstract double computePrice(ProductOrder productOrder); }
- 策略具体实现NormalActivity、DiscountActivity、VoucherActivity
/** * 无策略 */ public class NormalActivity extends Strategy { @Override public double computePrice(ProductOrder productOrder) { return productOrder.getPrice(); } } /** * 折扣策略 */ public class DiscountActivity extends Strategy { private double rate; public DiscountActivity(double rate) { this.rate = rate; } @Override public double computePrice(ProductOrder productOrder) { return productOrder.getPrice() * rate; } } /** * 折扣策略 */ public class VoucherActivity extends Strategy { private double voucher; public VoucherActivity(double voucher) { this.voucher = voucher; } @Override public double computePrice(ProductOrder productOrder) { if (this.voucher<productOrder.getPrice()){ return productOrder.getPrice() - this.voucher; }else{ return 0; } } }
- 策略上下文PromotionContext
/** * 策略调用上下文 */ public class PromotionContext { private Strategy strategy; public PromotionContext(Strategy strategy) { this.strategy = strategy; } public double executeStrategy(ProductOrder order){ return strategy.computePrice(order); } }
- 测试
ProductOrder order = new ProductOrder(888,1,1); PromotionContext context; //无优惠策略 context = new PromotionContext(new NormalActivity()); System.out.println(context.executeStrategy(order)); //折扣优惠策略 context = new PromotionContext(new DiscountActivity(0.8)); System.out.println(context.executeStrategy(order)); //满减优惠策略 context = new PromotionContext(new VoucherActivity(200)); System.out.println(context.executeStrategy(order));
(2)优点和缺点
- 优点
- 满足开闭原则,当增加新的具体策略时,不需要修改上下文类的代码,上下文就可以引用新的具体策略的实例
- 避免使用多重条件判断,,如果不用策略模式可能会使用多重条件语句不利于维护,和工厂模式的搭配使用可以很好地消除代码if-else的多层嵌套(工厂模式主要是根据参数,获取不同的策略)
- 缺点
- 策略类数量会增多,每个策略都是一个类,复用的可能性很小
- 对外暴露了类所有的行为和算法,行为过多导致策略类膨胀
14.行为型设计模式-模板方法模式
14.1.模板方法模式简介
(1)简介
- 模板方法模式Template Method:定义一个操作中的算法骨架,将算法的一些步骤延延迟到子类中,使得子类可以不改变该算法结构的情况下重新定义该算法的某些特定的步骤,属于行为模式。
- (2)应用场景
- javaweb里面的servlet,HttpService类提供了一个service()方法。
- 有多个子类共有逻辑相同的方法,可以考虑做为模板方法。
- 设计一个系统时知道了算法所需的关键步骤,且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,可以延迟到子类进行完成。
(3)角色
抽象模板(Abstract Template):定义一个模板方法,这个模板方法一般是一个具体的方法,给出一个顶级的算法骨架,而逻辑骨架的组成步骤在相应的抽象操作中,推迟到子类实现。
- 模板方法:定义了算法的骨架,按照某种顺序调用其包含的基本方法。
- 基本方法:是整个算法中的一个步骤,包括抽象方法和具体方法。
- 抽象方法:在抽象类中声明,有具体子类实现。
- 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它
- 具体模板(Concrete Template):实现父类所定义的一个或者多个抽象方法,他们是一个顶级算法逻辑的组成步骤。
(4)类关系图
- 创建抽象类,定义不同的模板执行的抽象方法,定义一个final类型的模板执行方法construction(),规定其他抽象方法的执行顺序。
- 创建具体的模板类实现抽象类的方法,coding()、test()、online()。
- 客户端调用父类的construction()方法,具体coding()、test()、online()调用子类的实现。
14.2.模板方法模式案例实战
(1)编码实战
- 顶层抽象模板类,定义方法的执行顺序AbstractProjectManager
public abstract class AbstractProjectManager { public final void construction(){ design(); coding(); test(); online(); } /** * 具体方法,需求设计 */ public void design(){ System.out.println("需求设计30天"); } /** * 抽象方法,编码,每个系统的编码不一样 */ public abstract void coding(); /** * 抽象方法,测试,每个系统的测试方式不一样 */ public abstract void test(); /** * 抽象方法,上线,每个系统的上线方式不一样 */ public abstract void online(); }
- 模板的具体实现类,PayServiceProjectManager 、UserServiceProjectManager
public class UserServiceProjectManager extends AbstractProjectManager { @Override public void coding() { System.out.println("编码50天"); } @Override public void test() { System.out.println("自动化测试、功能测试"); } @Override public void online() { System.out.println("全量上线"); } } public class PayServiceProjectManager extends AbstractProjectManager { @Override public void coding() { System.out.println("编码90天"); } @Override public void test() { System.out.println("自动化测试、功能测试、安全测试"); } @Override public void online() { System.out.println("灰度上线、AB双切"); } }
- 测试
AbstractProjectManager manager; manager = new PayServiceProjectManager(); manager.construction(); System.out.println("-------------------------"); manager = new UserServiceProjectManager(); manager.construction();
(2)优点和缺点
- 优点
- 扩展性好,对不变的代码进行封装,对可变的代码进行扩展,符合开闭原则。
- 提高代码复用性,将相同部分的代码放在抽象的父类中,将不同的代码放入不同的子类中。
- 通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反转控制。
- 缺点
- 每一个不同的实现都需要一个子类来实现,导致系统的类的个数增多。
(3)模板方法和建造者的区别
- 两者很大交集,建造者模式比模板方法模式多了一个指挥类,该类体现的是模板方法中抽象类的固定算法的共嗯那个,是一个创建对象的固定算法。
15.行为型设计模式-观察者模式
15.1.观察者模式简介
(1)简介
- 观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并且自动更新,也叫做发布订阅模式Publish/Subscribe,属于行为型模式。
(2)应用场景
- 消息通知:邮箱通知、微信朋友圈,监听观察事件。
- 当一个对象的改变需要同时改变其他对象,且他不知道具体有多少个对象有待改变的时候,考虑使用观察者模式。
- (3)角色
- Subject主题:持有多个观察者对象的引用,抽象主题提供了一个接口可以增加和删除观察者对象,有一个观察者数组,并实现增删以及通知操作。
- **Observer抽象观察者:**为具体观察者定义一个接口,在得到主题的通知时更新自己。
- **ConcreteSubject具体主题:**将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知。
- **ConcreteObserver具体观察者:**实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态保持一致。
(4)类关系图
- 定义抽象主题类Subject,其中有addObserver、deleteObserver、notifyAllObservers()方法,doSomethings()为抽象方法留给具体主题实现。
- 具体主题类ConcreteSubject实现doSomethings()
- 调用父类的notifyAllObservers(),通知观察者们,表示不同主题要做的具体事情。
- 定义抽象观察者接口Observer定义抽象方法update()。
- 创建具体的观察者,实现update()方法,具体观察者收到主题doSomething方法执行后具体的执行动作。
- 将观察者设置到主题中调用addObserver()方法。
15.2.观察者模式案例实战
(1)编码实战
- 抽象类主题Subject
public abstract class Subject { private List<Observer> list = new ArrayList<>(); /** * 增加观察者 */ public void addObserver(Observer observer){ list.add(observer); } /** * 删除观察者 */ public void deleteObserver(Observer observer){ list.remove(observer); } /** * 通知观察者 */ public void notifyAllObserver(){ for (Observer observer : list) { observer.update(); } } /** * 定义抽象方法,用于子类实现 */ public abstract void doSomething(); }
- 具体主题实现类BossConcreteSubject
public class BossConcreteSubject extends Subject { @Override public void doSomething(){ System.out.println("老板正在悄悄的走来"); //调用父类通知观察者 super.notifyAllObserver(); } }
- 观察者接口Observer
/** * 观察者接口 */ public interface Observer { /** * 抽象接口,当主题发生变化时,观察者监听随之发生响应 */ void update(); }
- 具体观察者实现类LXConcreteObserver、ZSConcreteObserver
public class ZSConcreteObserver implements Observer { @Override public void update() { System.out.println("张三收起了正在播放抖音的手机,开始假装敲码"); } } public class LXConcreteObserver implements Observer { @Override public void update() { System.out.println("李祥收起了正在播放抖音的手机,开始假装敲码"); } }
- 测试
//创建老板对象,主题对象 BossConcreteSubject subject = new BossConcreteSubject(); //创建张三对象,观察者对象 Observer zsConcreteObserver = new ZSConcreteObserver(); //创建李祥对象,观察者对象 Observer lxConcreteObserver = new LXConcreteObserver(); //将观察者加入主题 subject.addObserver(zsConcreteObserver); subject.addObserver(lxConcreteObserver); subject.doSomething();
(2)优点和缺点
- 优点
- 降低了目标与观察者之间的耦合关系,目标与观察者之间建立了一套触发机制。
- 观察者和被观察者是抽象耦合的。
- 缺点
- 观察者和观察目标之间有循环依赖的话,会触发他们之间进行循环调用,可能导致系统崩溃。
- 一个被观察者对象有很多的直接和简介的观察者的话,将所有的观察者都会收到通知的时候会花费很多时间。
16.行为型设计模式-责任链模式
16.1.责任链模式简介
(1)简介
- 责任链设计模式(Chain of Responsibility Pattern):客户端发出一个请求,链上的对象都有机会来处理这个请求,而客户端不需要知道谁是具体的处理对象。
- 让多个对象由机会处理请求,避免请求的发送者和接收者之间的耦合关系,将这个对象连成一条调用链,并沿着这个调用链传递该请求,直到有一个对象处理它才终止。符合条件处理请求,不符合传递到下一个节点。
(2)应用场景
- Apache Tomcat对Encoding编码处理,SpringBoot中的拦截器。
- 多个对象处理同一请求,但是具体由那个对象处理是运行时刻决定的,这种对象就可以用责任链模式。
- 日志输出级别debug->info->warning->error。
- 3)角色
- **Handler抽象处理者:**定义了一个处理请求的接口。
- **ConcreteHandler具体处理者:**处理所负责的请求,可访问它的后续节点,如果可处理该请求就处理,否则就转发给后续节点。
(4)类关系图
- 创建抽象类RiskControlManager持有自身的引用,用于设置更高一级的规则,抽象方法handler()用于不同的规则实现。
- 创建不同的规则实现类,实现handler(),如果符合规则就处理,部分和就调用父类的handler,会具体下发到某一个规则类中。
- 客户端调用
16.2.责任链模式案例实战
(1)业务流程图
(2)编码实战
- 请求实体类Request
public class Request { private String requestType; private int money; public String getRequestType() { return requestType; } public void setRequestType(String requestType) { this.requestType = requestType; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } public Request(String requestType, int money) { this.requestType = requestType; this.money = money; } public Request() { } }
- 请求类型枚举类控制指定请求时才会进入风控处理规则RequestType
public enum RequestType { ACCOUNT(10010,"转账"), PAY(10011,"支付"); private int code; private String name; public int getCode() { return code; } public void setCode(int code) { this.code = code; } RequestType(int code, String name) { this.code = code; this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } RequestType() { } }
- 抽象风控级别类RiskControlManager
public abstract class RiskControlManager { /** * 设置风控级别的名称 */ protected String name; /** * 设置更高一级的风控级别 */ protected RiskControlManager controlManager; public RiskControlManager(String name) { this.name = name; } public void setRiskControlManager(RiskControlManager controlManager){ this.controlManager = controlManager; } /** * 抽象方法,由具体的风控级别实现 */ public abstract void handler(Request request); }
- 具体风控实现类FirstRiskControlManager、SecondRiskControlManager、ThirdRiskControlManager
public class FirstRiskControlManager extends RiskControlManager { public FirstRiskControlManager(String name) { super(name); } @Override public void handler(Request request) { if(RequestType.valueOf(request.getRequestType())!=null && request.getMoney()<1000){ System.out.println("请输入支付密码"); System.out.println(name+":"+request.getRequestType()+",金额:"+request.getMoney()); }else{ //交给下一个节点处理 if(controlManager!=null){ controlManager.handler(request); } } } } public class SecondRiskControlManager extends RiskControlManager { public SecondRiskControlManager(String name) { super(name); } @Override public void handler(Request request) { if(RequestType.valueOf(request.getRequestType())!=null && request.getMoney()>=1000 && request.getMoney()<=10000){ System.out.println("请输入支付密码,确认短信验证码"); System.out.println(name+":"+request.getRequestType()+",金额:"+request.getMoney()); }else{ //交给下一个节点处理 if(controlManager!=null){ setRiskControlManager(controlManager); } } } } public class ThirdRiskControlManager extends RiskControlManager { public ThirdRiskControlManager(String name) { super(name); } @Override public void handler(Request request) { if(RequestType.valueOf(request.getRequestType())!=null && request.getMoney()>10000){ System.out.println("请输入支付密码,确认刷脸支付"); System.out.println(name+":"+request.getRequestType()+",金额:"+request.getMoney()); }else{ //交给下一个节点处理 if(controlManager!=null){ controlManager.handler(request); } } } }
- 测试
//先创建三个风控对象 RiskControlManager firstRiskControlManager = new FirstRiskControlManager("初级风控"); RiskControlManager secondRiskControlManager = new SecondRiskControlManager("中级风控"); RiskControlManager thirdRiskControlManager = new ThirdRiskControlManager("高级风控"); //风控规则建立联系,设置各自下一级风控级别 firstRiskControlManager.setRiskControlManager(secondRiskControlManager); secondRiskControlManager.setRiskControlManager(thirdRiskControlManager); Request request = new Request(RequestType.ACCOUNT.name(),1000); firstRiskControlManager.handler(request);
(3)优点和缺点
- 优点
- 客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者 降低了耦合度。
- 通过改变链内的调动它们的次序,允许动态地新增或者删除处理类,比较很方便维护。
- 增强了系统的可扩展性,可以根据需要增加新的请求处理类,满足开闭原则。
- 每个类只需要处理自己该处理的工作,明确各类的责任范围,满足单一职责原则。
- 缺点
- 处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。
- 不能保证请求一定被接收。
- 如果链路比较长,系统性能将受到一定影响,而且在进行代码调试时不太方便。
17.行为型设计模式-命令模式
17.1.命令模式简介
(1)简介
- 命令设计模式(Command Pattern):请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的对象,并把该命令传给相应的对象执行命令,属于行为型模式。
- 命令模式是一种特殊的策略模式,体现的是多个策略执行的问题,而不是选择的问题
- (2)应用场景
- 日常每个界面、按钮、键盘时间操作都是命令模式。
(3)角色
- **抽象命令(Command):**需要执行的所有命令都在这里生明。
- **具体命令(ConcreteCommand):**定义一个接收者和行为之间的弱耦合,实现execute()方法,负责调用接收者的相应操作,execute()方法通常叫做执行方法。
**接收者(Receiver):**负责具体实施和执行一个请求,命令传递到这里的是应该被执行的,实时和执行请求的方法叫做行动方法。
- **请求者(Invoker):**负责调用命令对象执行请求,相关的方法叫做行动方法。
- **客户端(Client):**创建一个具体命令(ConcreteCommand)对象确定其接收者。
(4)类关系图
创建命令的接收者,定义执行的方法on()、off()、change()。
创建命令接口Commond,定义抽象方法exectue()。
创建命令具体实现类,OnCommond、OffCommod、ChangeCommond实现exectue()方法持有命令接收者实例不同的命令实例执行不同的命令接收者中的方法。
- 创建命令请求者持有命令的实例,通过传入不同的命令示例接收者执行不同的命令操作。
17.2.命令模式案例实战
(1)编码流程
- 创建命令接收者TVReceiver
/** * 命令接收者 */ public class TVReceiver { public void on(){ System.out.println("电视开启"); } public void off(){ System.out.println("电视关闭"); } public void change(){ System.out.println("电视换台"); } }
- 命令的抽象接口,用于实现不同的具体命令Command
public interface Command { //命令执行方法 void execute(); }
- 具体的命令实现,ChangeCommand、OnCommand、OffCommand
/** * 具体命令实现类 */ public class OnCommand implements Command { /** * 设置命令的接收者 */ private TVReceiver receiver; public void setReceiver(TVReceiver receiver) { this.receiver = receiver; } public OnCommand(TVReceiver receiver) { this.receiver = receiver; } @Override public void execute() { System.out.println("执行开机命令:"); receiver.on(); } } /** * 具体命令实现类 */ public class OffCommand implements Command { /** * 设置命令的接收者 */ private TVReceiver receiver; public void setReceiver(TVReceiver receiver) { this.receiver = receiver; } public OffCommand(TVReceiver receiver) { this.receiver = receiver; } @Override public void execute() { System.out.println("执行关机命令:"); receiver.off(); } } /** * 具体命令实现类 */ public class ChangeCommand implements Command { /** * 设置命令的接收者 */ private TVReceiver receiver; public void setReceiver(TVReceiver receiver) { this.receiver = receiver; } public ChangeCommand(TVReceiver receiver) { this.receiver = receiver; } @Override public void execute() { System.out.println("执行换台命令:"); receiver.change(); } }
- 命令的发起者(请求者)AppInvoker
/** * 发起命令者 */ public class AppInvoker { /** * 设置需要执行的命令 */ private Command onCommand; private Command offCommand; private Command changeCommand; public void setOnCommand(Command onCommand) { this.onCommand = onCommand; } public void setOffCommand(Command offCommand) { this.offCommand = offCommand; } public void setChangeCommand(Command changeCommand) { this.changeCommand = changeCommand; } public void on(){ onCommand.execute(); } public void off(){ offCommand.execute(); } public void change(){ changeCommand.execute(); } }
- 测试
//创建命令的接收者 TVReceiver receiver = new TVReceiver(); //创建命令对象,绑定接收者,确定是哪一个接收者执行命令 Command onCommand = new OnCommand(receiver); Command offCommand = new OffCommand(receiver); Command changeCommand = new ChangeCommand(receiver); //创建命令发号者,绑定命令对象 AppInvoker appInvoker = new AppInvoker(); appInvoker.setChangeCommand(changeCommand); appInvoker.setOffCommand(offCommand); appInvoker.setOnCommand(onCommand); appInvoker.on(); System.out.println(); appInvoker.change(); System.out.println(); appInvoker.off();
(2)优点和缺点
- 优点
- 调用者角色与接收者角色之间没有任何依赖关系,不需要了解到底是那个接收者执行,降低了系统的耦合度。
- 扩展性强,新的命令可以很容易的添加到老的系统中去。
- 缺点
- 过多的命令模式会导致某些系统有过多的具体命令类。
18.行为型设计模式-迭代器模式
18.1.迭代器模式简介
(1)简介
- 迭代器设计模式(iterator Pattern):提供一种方法顺序访问一个聚合对象中各个元素,而又无需暴漏该对象的内部实现,属于行为型设计模式。
- JDK源码中集合框架用的最多,可以将集合看成一个容器,迭代器的作用就是把集合中的对象一个一个遍历出来。
(2)应用场景
- 迭代器模式是与集合共存的,只要实现一个集合,就需要同时提供这个集合的迭代器,就像java中的Collection,List,Map等都有自己的迭代器。
- 3)角色
- **抽象角色(Aggregate):**提供创建具体迭代器角色的接口,一般是接口,包好一个iterator()方法,例如java中的Collection接口,List接口,Set等。
**具体容器角色(ConcreteAggregate):**实现抽象容器的具体实现类,比如List接口的有序表现ArrayList。
**抽象迭代器角色(iterator):**负责定义访问和遍历元素的接口,包括几个核心方法,取得下一个元素的方法next(),判断是否遍历结束的方法isDone()。
- **具体迭代器角色(ConcreteIterator):**实现迭代器接口中定义的方法,并要记录遍历中的当前位置,完成集合的迭代。
(4)类关系图
- 创建抽象容器类,定义抽象方法add()、remove()、iterator()方法。
- 创建容器实现类实现add()、remove()、iterator()方法。
- 创建抽象迭代器类,Iterator,定义抽象方法hasNext()、next()。
- 创建具体迭代器类ConllectionIterator实现asNext()、next()方法。
18.2.迭代器模式案例实战
(1)编码实战
- 定义迭代器接口Iterator
public interface Iterator { /** * 判断是否有下一个元素 * @return */ boolean hasNext(); /** * 获取下一个元素 * @return */ Object next(); }
- 迭代器的具体实现类ConctreteIterator
public class ConcreteIterator implements Iterator { //定义一个List集合 private List list; //定义下标 private int index = 0; public ConcreteIterator(List list) { this.list = list; } //判断是否有下一个元素 @Override public boolean hasNext() { return index != list.size(); } //返回下一个元素 @Override public Object next() { if(this.hasNext()){ Object object = this.list.get(index); index++; return object; } return null; } }
- 定义容器接口ICollection
public interface ICollection { void add(Object object); void remove(Object object); Iterator iterator(); }
- 容器实现类MyCollection
public class MyCollection implements ICollection { private List list = new ArrayList(); @Override public void add(Object object) { list.add(object); } @Override public void remove(Object object) { list.remove(object); } @Override public Iterator iterator() { return new ConcreteIterator(list); } }
- 测试
ICollection collection = new MyCollection(); collection.add("李祥1号"); collection.add("李祥2号"); collection.add("李祥3号"); collection.add("李祥4号"); Iterator iterator = collection.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }
(2)优点和缺点
- 优点
- 可以做到不爆露集合的内部结构,又可让外部代码透明的访问集合内部的数据。
- 支持以不同的方式遍历一个聚合对象。
- 缺点
- 对于比较简单的遍历,使用迭代器遍历较为繁琐。
- 迭代器模式在遍历的同时更改迭代器所在的集合结构会导致出现异常。
19.行为型设计模式-备忘录模式
19.1.备忘录模式简介
(1)简介
- 备忘录设计模式(Memento Pattern):在不破坏封闭的前提下,获取一个对象的内部状态,保存对象的某个状态,以便在适当的时候恢复对象,又叫做快照模式,属于行为模式。
- 备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问。
- (2)应用场景
- git代码版本回滚
- 数据库事务回滚
- 虚拟机快照功能
(3)角色
- Originator:发起者,记录当前的内部状态,并负责创建和恢复备忘录数据,允许访问返回到先前状态的所有数据,可以根据需要决定Memento存储自己的哪些内部状态。
- Memento:备忘录,负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态,属性和Originator保持一致。
- Caretaker:管理者,对备忘录进行管理,保存和提供备忘录。
(4)类关系图
- 类的发起者SystemOriginator,其中包含saveSnapshot()保存当前状态、resumeSnapshot()返回之前保存的状态。
- 备忘录类持有和发起者类相同的属性,做存储。
- 备忘录管理者类持有SystemStateMemento实例,管理SystemStateMemento备忘。
19.2.备忘录模式案例实战
(1)编码实现
- 对象发起者SystemOrginator
/** * 对象发起者 */ public class SystemOriginator { private int storage = 100; public void display(){ System.out.println("当前内存剩余:"+storage); } public void run(){ this.storage = this.storage-10; } /** * 保存快照 */ public SystemStateMemento saveSnapshot(){ return new SystemStateMemento(storage); } /** * 恢复快照 */ public void resumeSnapshot(SystemStateMemento memento){ this.storage = memento.getStorage(); } }
- 备忘录类SystemStateMemento
/** * 备忘录类 */ public class SystemStateMemento { private int storage; public SystemStateMemento(int storage) { this.storage = storage; } public int getStorage() { return storage; } public void setStorage(int storage) { this.storage = storage; } }
- 备忘录类SystemStateCretaker
/** * 备忘录管理者 */ public class SystemStateCaretaker { private SystemStateMemento systemStateMemento; public SystemStateMemento getSystemStateMemento() { return systemStateMemento; } public void setSystemStateMemento(SystemStateMemento systemStateMemento) { this.systemStateMemento = systemStateMemento; } }
- 测试
//创建目标类,快照的发起者 SystemOriginator systemOriginator = new SystemOriginator(); System.out.println("开机"); systemOriginator.display(); System.out.println("添加快照"); //调用保存快照的方法 SystemStateMemento systemStateMemento = systemOriginator.saveSnapshot(); //将保存的快照设置到快照管理者中 SystemStateCaretaker systemStateCaretaker = new SystemStateCaretaker(); systemStateCaretaker.setSystemStateMemento(systemStateMemento); systemOriginator.run(); systemOriginator.run(); systemOriginator.run(); System.out.println("使用一段时间"); systemOriginator.display(); System.out.println("恢复快照"); systemOriginator.resumeSnapshot(systemStateCaretaker.getSystemStateMemento()); systemOriginator.display();
(2)优点和缺点
- 优点
- 给用户提供了一种可以恢复状态的机制。
- 实现了信息的封装,使得用户不需要关心状态的保存细节。
- 缺点
- 消耗更多的资源,而且每一次保存都会消耗一定的内存。
20.行为型设计模式-状态模式
20.1.状态模式简介
(1)简介
- 状态设计模式(State Pattern):对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为,属于行为模式。
- 允许一个对象在其内部状态改变时改变它的行为。
- 状态模式是策略模式的孪生兄弟,他们的UML图是一样的,但实际上解决的是不同情况的两种场景问题。
(2)应用场景
- 一个对像的行为取决于它的状态,并且他必须在运行时刻根据状态改变它的行为。
- 代码中包含大量与对象有关的条件语句,比如一个操作中含有庞大的多分支的if else语句,且这些分支依赖于该对象的状态。
- 电商订单状态:未支付、已支付、派送中等,各个状态处理不同的事情。
(3)角色
- Context上下文:定义了客户程序需要的接口并维护一个具体状态角色的实例,将于状态相关的操作委托给当前的ConcreteState对象来处理。
- State抽象状态类:定义一个接口以封装与Context的一个特定状态相关的行为。
- ConcreteState具体状态类:实现抽象状态定义的接口。
(4)类关系图
- 状态接口OrderState,定义抽象方法handler()。
- 具体的状态实现类NewOrderState、PayOrderState、SendOrderState实现OrderState接口实现具体的handler方法。
- OrderContext上下文类,持有状态引用,调用set方法中调用不同状态中的handler方法。
20.2.状态模式案例实战
(1)编码实现
- 定义状态接口State
/** * 状态接口 */ public interface OrderState { /** * 子类实现,不用的状态处理逻辑方法 */ void handler(); }
- 定义状态的具体实现NewOrderState、PayOrderState、SendOrderState
public class NewOrderState implements OrderState { @Override public void handler() { System.out.println("新订单入库"); System.out.println("调用客服服务"); } } public class PayOrderState implements OrderState { @Override public void handler() { System.out.println("订单支付"); System.out.println("调用客服服务"); } } public class SendOrderState implements OrderState { @Override public void handler() { System.out.println("订单发货"); System.out.println("调用客服服务"); System.out.println("调用短信服务"); } }
- 定义状态的上下文OrderContext
/** * 状态上下文,设置不同的状态执行不同的逻辑 */ public class OrderContext { private State state; public void setState(State state) { this.state = state; System.out.println("当前状态为:"+state.getClass().toString().substring(state.getClass().toString().lastIndexOf(".")+1)); state.handler(); System.out.println(); } }
- 测试
OrderContext context = new OrderContext(); context.setState(new NewOrderState()); context.setState(new PayOrderState()); context.setState(new SendOrderState());
(2)优点和缺点
- 优点
- 只需要改变对象状态即可改变对象的行为。
- 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
- 缺点
- 状态模式的使用会增加系统类和对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码混乱。
- 对开闭原则不是很友好。
- 状态模式和策略模式的区别
- 状态模式关注的是对于不同的状态做不同的事情,不同状态不能切换。
- 策略模式是针对同一种事情的不同解决办法,不同的策略可以进行切换。
21.不常用的设计模式
21.1.访问者模式
(1)简介
- 访问者模式(Visitor Pattern):使用一个访问者类,改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者的改变而改变,属于行为型设计模式。
(2)应用场景
- 将数据结构与数据操作分离。
- 稳定数据结构和一边的操作耦合问题。
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
(3)优点和缺点
- 优点
- 符合单一职责,可扩展性,灵活性。
- 缺点
- 具体元素对访问者公布细节,违反了迪米特法则。
- 违反依赖倒转原则,没有依赖于抽象。
- 具体元素变更比较困难。
21.2.中介者模式
(1)简介
- 中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
(2)应用场景
- 系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。
- 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
(3)优点和缺点
- 优点
- 降低了类的复杂度,将一对多转化成了一对一。
- 各个类之间的解耦。
- 符合迪米特原则。
- 缺点
- 中介者会庞大,变得复杂难以维护。
21.3.解释器模式
(1)简介
- 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
(2)应用场景
- 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
- 一些重复出现的问题可以用一种简单的语言来进行表达。
- 一个简单语法需要解释的场景。
(3)优点和缺点
- 优点
- 可扩展性比较好,灵活。
- 增加了新的解释表达式的方式。
- 易于实现简单文法。
- 缺点
- 可利用场景比较少。
- 对于复杂的文法比较难维护。
- 解释器模式会引起类膨胀。
- 解释器模式采用递归调用方法。
22.设计模式在源码和框架的应用
22.1.JDK源码中的设计模式
- 迭代器设计模式:Iterator类
- 装饰器设计模式:BufferedInputStream类
- 单例设计模式:Runtime类
- 建造者设计模式:StringBuilder类
- 适配器设计模式:JDBC数据库驱动
- 享元设计模式:String类
- 策略设计模式:Comparator接口常用的compare()方法