目录
行为型模式
重点关注对象之间的通信
1、观察者模式
1.1 定义
观察者(Observer)模式定义:只多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖他的对象都得到通知并被自动更新,这种模式有时又称作发布-订阅模式、模型-视图模式,它是行为型模式。
1.2 结构角色
实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。
观察者模式的主要角色如下。
-
抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的集合变量和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
-
具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
-
抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
-
具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
1.3 应用案例
利用观察者模式设计一个程序,分析“人名币”的升值或贬值对进口公司的进口产品成本或出口公司的出口产品收入以及公司的利润率的影响。
分析:当“人民币汇率”升值时,进口公司的进口产品成本降低且利润率提升,出口公司的出口产品收入降低且利润率降低;当“人民币汇率”贬值时,进口公司的进口产品成本提升且利润率降低,出口公司的出口产品收入提升且利润率提升。
-
汇率(Rate)类是抽象目标类,它包含了保存观察者(Company)的 List 和增加/删除观察者的方法,以及有关汇率改变的抽象方法 change(int number);
-
人民币汇率(RMBrate)类是具体目标, 它实现了父类的 change(int number) 方法,即当人民币汇率发生改变时通过相关公司;
-
公司(Company)类是抽象观察者,它定义了一个有关汇率反应的抽象方法 response(int number);
-
进口公司(ImportCompany)类和出口公司(ExportCompany)类是具体观察者类,它们实现了父类的 response(int number) 方法,即当它们接收到汇率发生改变的通知时作为相应的反应。
类图结构:
1.4 实现代码
抽象目标:汇率
public abstract class Rate {
protected List<Company> companys = new ArrayList<Company>();
//增加观察者方法
public void add(Company company){
companys.add(company);
}
//删除观察者方法
public void remove(Company company){
companys.remove(company);
}
//汇率改变
public abstract void change(int number);
}
具体目标:人名币汇率
public class RMBrate extends Rate {
@Override
public void change(int number) {
for(Company obs : companys){
obs.response(number);
}
}
}
抽象观察者:公司
public interface Company {
public void response(int number);
}
具体观察者:进口公司和出口公司
public class ImportCompany implements Company {
public void response(int number) {
if(number > 0){
System.out.println("人民币汇率升值"+number+"个基点,降低了进口产品成本,提升了进口公司利润率。");
}else if(number < 0){
System.out.println("人民币汇率贬值"+(-number)+"个基点,提升了进口产品成本,降低了进口公司利润率。");
}
}
}
public class ExportCompany implements Company {
@Override
public void response(int number) {
if(number > 0){
System.out.println("人民币汇率升值"+number+"个基点,降低了出口产品收入,降低了出口公司的销售利润率。");
}else if(number < 0){
System.out.println("人民币汇率贬值"+(-number)+"个基点,提升了出口产品收入,提升了出口公司的销售利润率。");
}
}
}
测试
public class RMBrateTest {
public static void main(String[] args) {
Rate rate = new RMBrate();
Company watcher1 = new ImportCompany();
Company watcher2 = new ExportCompany();
rate.add(watcher1);
rate.add(watcher2);
rate.change(10);
rate.change(-9);
}
}
2、责任链模式
2.1 定义
责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。
在这种模式中,通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推(击鼓传花)。
2.2 结构角色
主要包含以下角色:
-
抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
-
具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
-
客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
类图结构
2.3 应用案例
用责任链模式设计一个请假条审批模块。
分析:假如规定学生请假小于或等于 2 天,班主任可以批准;小于或等于 7 天,系主任可以批准;小于或等于 10 天,院长可以批准;其他情况不予批准;这个实例适合使用职责链模式实现。
-
首先,定义一个领导类(Leader),它是抽象处理者,包含了一个指向下一位领导的指针next 和一个处理假条的抽象处理方法 handleRequest(int LeaveDays);
-
然后,定义班主任类(ClassAdviser)、系主任类(DepartmentHead)和院长类(Dean),它们是抽象 处理者的子类,是具体处理者,必须根据自己的权力去实现父类的 handleRequest(int leaveDays) 方法,如果无权处理就将假条交给下一位具体处理者
-
最后;客户类负责创建处理链,并将假条交给链头的具体处理者(班主任)。
结构图如下
2.4 代码实现
抽象处理者:领导类
public abstract class Leader {
private Leader next;
public Leader getNext() {
return next;
}
public void setNext(Leader next) {
this.next = next;
}
//处理请求的方法
public abstract void handleRequest(int leaveDays);
}
具体处理者1:班主任类
public class ClassAdviser extends Leader {
@Override
public void handleRequest(int leaveDays) {
if(leaveDays <= 2){
System.out.println("班主任批准您请假:"+leaveDays+"天");
}else{
if(getNext() != null){
getNext().handleRequest(leaveDays);
}else{
System.out.println("请假天数太多,班主任没有权限批准该假条!");
}
}
}
}
具体处理者2:系主任类
public class DepartmentHead extends Leader {
@Override
public void handleRequest(int leaveDays) {
if(leaveDays <= 7){
System.out.println("系主任批准您请假:"+leaveDays+"天");
}else{
if(getNext() != null){
getNext().handleRequest(leaveDays);
}else{
System.out.println("请假天数太多,系主任无权限批准!");
}
}
}
}
具体处理者3:院长类
public class Dean extends Leader {
public void handleRequest(int LeaveDays) {
if (LeaveDays <= 10) {
System.out.println("院长批准您请假" + LeaveDays + "天。");
} else {
if (getNext() != null) {
getNext().handleRequest(LeaveDays);
} else {
System.out.println("请假天数太多,院长没有权限批准!");
}
}
}
}
请假流程测试
public class LeaveApprovalTest {
public static void main(String[] args) {
//组装责任链
Leader teacher1 = new ClassAdviser();
Leader teacher2 = new DepartmentHead();
Leader teacher3 = new Dean();
teacher1.setNext(teacher2);
teacher2.setNext(teacher3);
//提交请求
teacher1.handleRequest(11);
}
}
3、模板模式
3.1 定义介绍
在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:一些方法通用,却在每一个子类都重新写了这一方法。
何时使用:有一些通用的方法。
如何解决:将这些通用算法抽象出来。
关键代码:在抽象类实现,其他步骤在子类实现。
应用实例: 1、在造房子的时候,地基、走线、水管都一样,只有在建筑的后期才有加壁橱加栅栏等差异。 2、西游记里面菩萨定好的 81 难,这就是一个顶层的逻辑骨架。 3、spring 中对 Hibernate 的支持,将一些已经定好的方法封装起来,比如开启事务、获取 Session、关闭 Session 等,程序员不重复写那些已经规范好的代码,直接丢一个实体就可以保存。
优点: 1、封装不变部分,扩展可变部分。 2、提取公共代码,便于维护。 3、行为由父类控制,子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
使用场景: 1、有多个子类共有的方法,且逻辑相同。 2、重要的、复杂的方法,可以考虑作为模板方法。
注意事项:为防止恶意操作,一般模板方法都加上 final 关键词。
3.2 应用案例
游戏包括足球和板球等,但是每个游戏的操作过程都是一样的(初始化游戏,开始游戏,结束游戏),那么我们可以将整个操作过程作为一个模板,具体的过程延迟到子类去实现
主要角色:
-
抽象游戏类(Game),包括规范操作:初始化游戏,开始游戏,结束游戏和play模板
-
具体游戏类(Cricket,Football),具体实现规范操作
我们将创建一个定义操作的 Game 抽象类,其中,模板方法设置为 final,这样它就不会被重写。Cricket 和 Football 是扩展了 Game 的实体类,它们重写了抽象类的方法。
3.3 代码实现
抽象类定义操作:Game
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//模板
public final void play(){
//初始化游戏
initialize();
//开始游戏
startPlay();
//结束游戏
endPlay();
}
}
游戏具体实现类:
public class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
测试,子类调用父类模板方法,父类模板调用子类具体实现
public class TemplatePatternDemo {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
}
}
4、迭代器模式
4.1 定义介绍
这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
主要解决:不同的方式来遍历整个整合对象。
何时使用:遍历一个聚合对象。
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
关键代码:定义接口:hasNext, next。
应用实例:JAVA 中的 iterator。
优点: 1、它支持以不同的方式遍历一个聚合对象。 2、迭代器简化了聚合类。 3、在同一个聚合上可以有多个遍历。 4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点:由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
使用场景: 1、访问一个聚合对象的内容而无须暴露它的内部表示。 2、需要为聚合对象提供多种遍历方式。 3、为遍历不同的聚合结构提供一个统一的接口。
注意事项:迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
4.2 应用案例
我们将创建一个叙述导航方法的 Iterator 接口和一个返回迭代器的 Container 接口。实现了 Container 接口的实体类将负责实现 Iterator 接口。我们的演示类使用实体类 NamesRepository 来打印 NamesRepository 中存储为集合的 Names。
主要角色:
-
抽象容器角色(Container):负责提供创建具体迭代器角色的接口,一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等。
-
具体容器角色(NameRepository):就是实现抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkedList,Set接口的哈希列表的实现HashSet等。
-
抽象迭代器角色(Iterator):负责定义访问和遍历元素的接口。
-
具体迭代器角色(NameIterator):实现迭代器接口,并要记录遍历中的当前位置。
4.3 代码实现
抽象容器角色:Container
public interface Container {
public Iterator getIterator();
}
抽象迭代器角色:Iterator
public interface Iterator {
public boolean hasNext();
public Object next();
}
具体容器实现:NameRepository
public class NameRepository implements Container {
public Iterator getIterator() {
return new NameIterator();
}
}
具体迭代器角色:NameList
public class NameList implements Iterator {
public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
int index;
public boolean hasNext() {
if (index < names.length) {
return true;
}
return false;
}
public Object next() {
if (this.hasNext()) {
return names[index++];
}
return null;
}
}
测试,遍历聚合结构(你将完全看不到集合的内部结构):
public class IteratorPatternDemo {
public static void main(String[] args) {
NameRepository namesRepository = new NameRepository();
Iterator iter = namesRepository.getIterator();
while (iter.hasNext()){
String name = (String)iter.next();
System.out.println("Name : " + name);
}
}
}
一句话总结:迭代器模式就是抽象一个迭代器类来分离了集合对象的遍历行为,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据