本片就是java设计模式最后一篇了,前面分别介绍了五种创建型设计模式,和七种结构型设计模式,还有五种行为模式,本篇将介绍剩下的七种行为模式
创建型模式
结构型模式
行为模式
21备忘录模式
21.1备忘录模式的定义
在不破坏封装的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
这个模式就相当于注销的功能一样,回到之前的状态
21.2备忘录模式的结构
- Originator(原发器):一个普通类,通过这个类去创建一个备忘录
- Memento(备忘录):用于储存原发器的内部状态,根据原发器来保存那些内部状态
- Caretaker(负责人):管理者,负责保存备忘,不能对其进行操作或检查
21.3备忘录模式的实现
我们用下象棋来模仿一下备忘录模式,
我们先写一个象棋的原发器
public class Chessman {
private String label;
private int x;
private int y;
public Chessman(String label, int x, int y) {
this.label = label;
this.x = x;
this.y = y;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public ChessmanMemento save(){
return new ChessmanMemento(this.label,this.x,this.y);
}
public void restore(ChessmanMemento memento){
this.label=memento.getLabel();
this.x=memento.getX();
this.y=memento.getY();
}
}
再写一个象棋的备忘录棋子
public class ChessmanMemento {
private String label;
private int x;
private int y;
public ChessmanMemento(String label, int x, int y) {
this.label = label;
this.x = x;
this.y = y;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
再写控制类,保存棋子的备忘录
public class MementoCaretaker {
private ChessmanMemento memento;
public ChessmanMemento getMemento() {
return memento;
}
public void setMemento(ChessmanMemento memento) {
this.memento = memento;
}
}
测试类
public class Client {
public static void main(String[] args) {
MementoCaretaker caretaker = new MementoCaretaker();
Chessman chessman =new Chessman("马",1,1);
display(chessman);
caretaker.setMemento(chessman.save());
chessman.setX(5);
display(chessman);
caretaker.setMemento(chessman.save());
chessman.setY(9);
display(chessman);
System.out.println("========悔棋============");
chessman.restore(caretaker.getMemento());
display(chessman);
//棋子马当前位置:第1行第1列
//棋子马当前位置:第5行第1列
//棋子马当前位置:第5行第9列
//========悔棋============
//棋子马当前位置:第5行第1列
}
public static void display(Chessman chessman){
System.out.println("棋子"+chessman.getLabel()+"当前位置:第"
+chessman.getX()+"行"+"第"+chessman.getY()+"列");
}
}
21.4实现多次撤销
我们之前的保存备忘录的类只能保存一个实例对象,所以返回的都是上次状态,我们将这个成员变量设置为一个集合,就可以实现多次撤销
public class MementoCaretaker2 {
private ArrayList<ChessmanMemento> mementosList = new ArrayList<>();
public ChessmanMemento getMemento(int i){
return mementosList.get(i);
}
public void setMemento(ChessmanMemento memento){
mementosList.add(memento);
}
}
测试时就可以输入状态选择来撤销到哪一步
public class Client2 {
private static MementoCaretaker2 caretaker = new MementoCaretaker2();
private static int index=-1;
public static void main(String[] args) {
Chessman chessman =new Chessman("象",2,1);
play(chessman);
chessman.setY(8);
play(chessman);
chessman.setX(7);
play(chessman);
undo(chessman,index);
undo(chessman,index);
redo(chessman,index);
redo(chessman,index);
//棋子象当前位置:第2行第1列
//棋子象当前位置:第2行第8列
//棋子象当前位置:第7行第8列
//===悔棋===
//棋子象当前位置:第2行第8列
//===悔棋===
//棋子象当前位置:第2行第1列
//====撤销悔棋===
//棋子象当前位置:第2行第8列
//====撤销悔棋===
//棋子象当前位置:第7行第8列
}
public static void play(Chessman chessman){
caretaker.setMemento(chessman.save());
index++;
System.out.println("棋子"+chessman.getLabel()+"当前位置:第"
+chessman.getX()+"行"+"第"+chessman.getY()+"列");
}
public static void undo(Chessman chessman,int i){
System.out.println("===悔棋===");
index--;
chessman.restore(caretaker.getMemento(i-1));
System.out.println("棋子"+chessman.getLabel()+"当前位置:第"
+chessman.getX()+"行"+"第"+chessman.getY()+"列");
}
public static void redo(Chessman chessman,int i){
System.out.println("====撤销悔棋===");
index++;
chessman.restore(caretaker.getMemento(i+1));
System.out.println("棋子"+chessman.getLabel()+"当前位置:第"
+chessman.getX()+"行"+"第"+chessman.getY()+"列");
}
}
21.5备忘录模式的优缺点
- 优点:提供一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,此外它还实现了对信息的封装
- 缺点:资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免地占用大量的存储资源。
21.6备忘录模式的适用环境
保存一个对象在某一时刻的全部状态或者部分状态,这样以后需要时能够恢复它之前的状态,实现撤销操作,防止对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴漏给外界对象
22观察者模式
22.1观察者模式的定义
定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时相关依赖对象皆得到通知并被自动更新
举个例子在日常交通的过程中汽车和红绿灯的关系,就很像观察者模式,红绿灯是观察对象,汽车是观察者,随着红绿灯的变化,汽车的行为也随之变化。它定义了对象之间一对多的依赖关系,让一个对象的改变能够影响其他对象。
22.2观察者模式的结构
- Subject(目标):它指的是被观察的对象,可以是接口也可以是抽象类
- ConcreteSubject(具体目标):具体目标是目标的子类当状态发生变化时,向每个观察者发出通知
- Observer(观察者):一般定义为接口,对观察目标的改变做出反应。
- ConcreteObserver(具体观察类):在具体观察者中维护一个指向具体目标对象的引用,它用于存储具体观察者的有关状态。
22.3观察者模式的实现
当在玩游戏的时候战队之间打比赛时,当队友受到攻击去挨个通知盟友太慢,就需要一个指挥部来做这样的事,专门处理请求信息,发布求救信息。
首先我们先写一个一个抽象的指挥者,作为抽象目标类
public abstract class AllControlCenter {
protected String allyName;
protected ArrayList<Observer> players = new ArrayList<>();
public void join(Observer observer){
System.out.println(observer.getName()+"加入"+this.allyName+"战队!");
players.add(observer);
}
public void quit(Observer observer){
System.out.println(observer.getName()+"退出"+this.allyName+"战队!");
}
public abstract void notifyObserver(String name);
}
再写一个具体的指挥部,来充当具体目标类
public class ConcreteAllyControlCenter extends AllControlCenter {
public ConcreteAllyControlCenter(String name) {
this.allyName=name;
System.out.println(name+"战队组建成功");
System.out.println("------------------");
}
@Override
public void notifyObserver(String name) {
System.out.println(this.allyName+"战队紧急通知盟友"+name+"受到敌人攻击");
for(Observer obs: players){
if(!obs.getName().equalsIgnoreCase(name)){
obs.help();
}
}
}
}
Observer 抽象观察者类
public interface Observer {
public String getName();
public void setName(String name);
public void help();
public void beAttacked(AllControlCenter acc);
}
再写具体的战队成员类,充当具体观察者
public class Player implements Observer {
private String name;
public Player(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public void setName(String name) {
this.name=name;
}
@Override
public void help() {
System.out.println("坚持住,"+this.name+"来救你了");
}
@Override
public void beAttacked(AllControlCenter acc) {
System.out.println(this.name+"被攻击!");
acc.notifyObserver(name);
}
}
测试
public class Client {
public static void main(String[] args) {
AllControlCenter acc =new ConcreteAllyControlCenter("渣渣五人组");
Observer player1 = new Player("高海峰");
acc.join(player1);
Observer player2 = new Player("李杰");
acc.join(player2);
Observer player3 = new Player("刘垣灼");
acc.join(player3);
Observer player4 = new Player("李志远");
acc.join(player4);
Observer player5 = new Player("范清珅");
acc.join(player5);
System.out.println("---------------");
//受到攻击
player1.beAttacked(acc);
//渣渣五人组战队组建成功
//------------------
//高海峰加入渣渣五人组战队!
//李杰加入渣渣五人组战队!
//刘垣灼加入渣渣五人组战队!
//李志远加入渣渣五人组战队!
//范清珅加入渣渣五人组战队!
//---------------
//高海峰被攻击!
//渣渣五人组战队紧急通知盟友高海峰受到敌人攻击
//坚持住,李杰来救你了
//坚持住,刘垣灼来救你了
//坚持住,李志远来救你了
//坚持住,范清珅来救你了
}
}
22.4观察者模式和java事件的处理
- 在早期的jdk中有提供Observer接口,提供开发者去实现,还有提供了Observable类作为观察者模式的抽象层,再自定义具体观察者目标类。
- 在DEM模型中的目标角色负责发布事件,而观察者角色可以向目标所订阅它所感兴趣的事件。当一个具体目标产生一个时间时他将通知所有订阅者。
22.5观察者模式与MVC
当前流行的MVC框架中也应用了观察者模式,MVC是一个架构模式,包含三个角色,即模型(Model),视图(View)和控制者(Controller)。其中模型可对应观察者模式中的观察目标,而视图对应于观察者,控制器纯当两者之间的中介者。当模型层的数据发生改变时视图层将自动改变其显示内容。
22.6观察者模式的优缺点
- 优点:可以实现表示层和数据逻辑层的分离;在观察目标和观察者之间建立一个抽象的耦合,支持广播通信且符合开闭原则
- 缺点:把所以的观察者都通知到需要花费很多时间,没有相应的机制告诉观察者目标的发生的变化。
22.7观察者模式适用环境
一个抽象模型有两个方面,其中一个依赖于另一个方面,将这两个方面封装在独立对象中使他们可以独自改变和复用。一个对象的改变而导致多个其他对象而发生改变,在系统中创建一个触发链。
23状态模式
23.1状态模式的定义
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类
举个例子就是水在不同的环境下的状态是不同,但本质是相同的,随着温度的改变而变换状态
23.2状态模式的定义
- Context(环境类):环境类又称上下文,它拥有多种状态的对象。在环境的多样性下切换不同的状态
- State(抽象状态类):用于定义一个接口封装与环境类的一个特定状态相关的行为。
- ConcreteState(具体状态类):它是实现抽象接口的类,每个类对应每种环境的一种状态。
23.3状态模式的实现
比如我们在银行存钱,和查询余额的时候,就有不同的状态,有正常状态,透支状态,受限状态。随着金额而变化,而进行状态的转变。
我们先写一个银行环境类
public class Account {
private AccountState state;
private String owner;
private double balance = 0;
public Account(String owner, double init){
this.owner = owner;
this.balance =init ;
this.state = new NormalState(this);
System.out.println(this.owner+"开户,初始金额为"+init);
System.out.println("--------------");
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void setState(AccountState state) {
this.state = state;
}
public void deposit(double amount){
System.out.println(this.owner+"存款"+amount);
state.deposit(amount);
System.out.println("现在余额为"+this.balance);
System.out.println("现在账户状态是"+this.state.getClass().getName());
System.out.println("----------------");
}
public void withraw(double amount){
System.out.println(this.owner+"取款"+amount);
state.withdraw(amount);
System.out.println("现在余额为"+this.balance);
System.out.println("现在账户状态是"+this.state.getClass().getName());
System.out.println("----------------");
}
public void computeInterest(){
state.computeInterest();
}
}
再写抽象状态类
public abstract class AccountState {
protected Account acc ;
public abstract void deposit(double amount);
public abstract void withdraw(double amount);
public abstract void computeInterest();
public abstract void stateCheck();
}
再分别实现它的状态子类
public class NormalState extends AccountState {
public NormalState(Account account) {
this.acc = account;
}
public NormalState(AccountState state) {
this.acc =state.acc;
}
@Override
public void deposit(double amount) {
acc.setBalance(acc.getBalance()+amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
acc.setBalance(acc.getBalance()-amount);
stateCheck();
}
@Override
public void computeInterest() {
System.out.println("正常状态,无须利息");
}
@Override
public void stateCheck() {
if(acc.getBalance()>-2000 && acc.getBalance()<=0){
acc.setState(new OverdraftState(this));
}else if (acc.getBalance()==-2000){
acc.setState(new RestrictedState(this));
}else if(acc.getBalance()<-2000){
System.out.println("操作受限");
}
}
}
public class OverdraftState extends AccountState {
public OverdraftState(AccountState state) {
this.acc=state.acc;
}
@Override
public void deposit(double amount) {
acc.setBalance(acc.getBalance()+amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
acc.setBalance(acc.getBalance()-amount);
stateCheck();
}
@Override
public void computeInterest() {
System.out.println("计算利息");
}
@Override
public void stateCheck() {
if(acc.getBalance()>0){
acc.setState(new NormalState(this));
}else if (acc.getBalance()==-2000){
acc.setState(new RestrictedState(this));
}else if(acc.getBalance()<-2000){
System.out.println("操作受限");
}
}
}
public class RestrictedState extends AccountState{
public RestrictedState(AccountState state) {
this.acc = state.acc;
}
@Override
public void deposit(double amount) {
acc.setBalance(acc.getBalance()+amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
acc.setBalance(acc.getBalance()-amount);
stateCheck();
}
@Override
public void computeInterest() {
System.out.println("计算利息");
}
@Override
public void stateCheck() {
if(acc.getBalance()>0){
acc.setState(new NormalState(this));
}else if (acc.getBalance()>-2000) {
acc.setState(new OverdraftState(this));
}
}
}
三个状态分别是正常状态,透支状态,和受限状态,进行测试
public class Client {
public static void main(String[] args) {
Account acc =new Account("李杰",0.0);
acc.deposit(1000);
acc.withraw(2000);
acc.deposit(1000);
acc.withraw(4000);
acc.withraw(1000);
acc.computeInterest();
//李杰开户,初始金额为0.0
//--------------
//李杰存款1000.0
//现在余额为1000.0
//现在账户状态是com.itcast.StatePattern.test1.NormalState
//----------------
//李杰取款2000.0
//现在余额为-1000.0
//现在账户状态是com.itcast.StatePattern.test1.OverdraftState
//----------------
//李杰存款1000.0
//现在余额为0.0
//现在账户状态是com.itcast.StatePattern.test1.OverdraftState
//----------------
//李杰取款4000.0
//操作受限
//现在余额为-4000.0
//现在账户状态是com.itcast.StatePattern.test1.OverdraftState
//----------------
//李杰取款1000.0
//操作受限
//现在余额为-5000.0
//现在账户状态是com.itcast.StatePattern.test1.OverdraftState
//----------------
//计算利息
}
}
23.4共享状态
在有些情况下,多个环境对象可能需要共享一个状态,如果希望在系统中实现多个环境对象共享一个或多个状态对象,那么需要将这些状态对象定义为环境类的静态成员对象
比如一个是在系统中的开关对象必须要求是一致的,同时开或者同时关闭
public class Switch {
private static SwitchState currentState ,onState ,offState;
private String name;
public Switch(String name){
this.name=name;
onState = new OnState();
offState = new OffState();
currentState = onState;
}
public void setState(SwitchState state){
currentState =state;
}
public static SwitchState getState(String type){
if(type.equalsIgnoreCase("on")){
return onState;
}else {
return offState;
}
}
public void on(){
System.out.print(name);
currentState.on(this);
}
public void off(){
System.out.print(name);
currentState.off(this);
}
}
开关状态
public abstract class SwitchState {
public abstract void on(Switch s);
public abstract void off(Switch s);
}
public class OffState extends SwitchState {
@Override
public void on(Switch s) {
System.out.println("打开");
s.setState(Switch.getState("on"));
}
@Override
public void off(Switch s) {
System.out.println("已经关闭");
}
}
public class OnState extends SwitchState {
@Override
public void on(Switch s) {
System.out.println("开关已经打开");
}
@Override
public void off(Switch s) {
System.out.println("关闭");
s.setState(Switch.getState("off"));
}
}
测试
public class Client {
public static void main(String[] args) {
Switch s1,s2;
s1 = new Switch("开关一");
s2 = new Switch("开关二");
s1.on();
s2.on();
s1.off();
s2.off();
s2.on();
s1.on();
//开关一开关已经打开
//开关二开关已经打开
//开关一关闭
//开关二已经关闭
//开关二打开
//开关一开关已经打开
}
}
这个实例就是将开关状态设置为静态变量,保证了其的实例化对象,在创建的时候只有一个状态实例,然后当一个开关改变状态的时候,另一个开关 的状态也随之而改变
23.5状态模式的优缺点
- 优点:它封装了状态的转换规则,可以对状态转换代码进行集中管理,而不是分散在每个业务方法中,允许状态和转换逻辑合为一体,而不是提供一个巨大的条件语句块,可以让多个对象共享一个状态对象,从而减少系统中对象的个数
- 缺点:状态模式会增加系统中类和对象的个数,导致系统开销增大,对于开闭原则不好。
23.6状态模式的适用环境
对象行为依赖他的状态,状态的改变导致行为的变化
24策略模式
24.1策略模式的定义
定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。策略模式让算法可以独立使用它的客户而变化。
举个例子,我们在外出旅游的时候去某个目的地的途径一般不止一条,游客可以选择不同的出行方式,骑车,开车,坐火车,坐飞机。我就可以根据不同的需求去选择不同的出行方式
24.2策略模式的结构
- Context(环境类):环境类是使用算法的角色,在解决某个问题的时候可以采用多种策略
- Strategy(抽象策略类):抽象策略类为所支持的算法声明了抽象方法,是所有策略类的父类。可以是抽象类或具体类,也可以是接口。
- ConcreteStrategy(具体策略类):实现了抽象策略类中声明的算法。
24.3策略模式的实现
某个电影公司要出现一系列优惠政策,对于不同人群有不同的折扣,学生用学生证可以打8折,儿童可以减价十元,影院vip可以半价,而且有积分。系统会通过不同的人群选择不同的策略来计算价格。
首先我们去写环境类,电影票类
public class MovieTicket {
private double price;
private Discount discount;
public void setPrice(double price){
this.price=price;
}
public void setDiscount(Discount discount){
this.discount = discount;
}
public double getPrice(){
return discount.calculate(this.price);
}
}
再写折扣的抽象类,以及它 的不同实现类
public interface Discount {
double calculate(double price);
}
public class StudentDiscount implements Discount {
private final double DISCOUNT = 0.8;
@Override
public double calculate(double price) {
System.out.println("学生票");
return price*DISCOUNT;
}
}
public class ChildrenDiscount implements Discount {
private final double DISCOUNT = 10;
@Override
public double calculate(double price) {
System.out.println("儿童票:");
if(price>20.0){
return price-10;
}
return price;
}
}
public class VIPDiscount implements Discount {
private final double DISCOUNT = 0.5;
@Override
public double calculate(double price) {
System.out.println("VIP票:");
System.out.println("增加积分");
return price*DISCOUNT;
}
}
然后我们和之前一样使用xml工具类来解决,将类的全路径写入到xml的配置文件中,利用xml配置类来获取这个类对象。
<?xml version="1.0" ?>
<config>
<className>com.itcast.StrategyPattern.test1.ChildrenDiscount</className>
<className>com.itcast.StrategyPattern.test1.StudentDiscount</className>
<className>com.itcast.StrategyPattern.test1.VIPDiscount</className>
</config>
public class XMLUtil {
public static Object getBean(int index){
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new File("src//com//itcast//StrategyPattern//config.xml"));
NodeList className = document.getElementsByTagName("className");
Node node = className.item(index).getFirstChild();
String name = node.getNodeValue();
Class c = Class.forName(name);
Object o = c.newInstance();
return o;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
测试,就只需要改不同的getBean索引就可以得到不同的类,获取不同的折扣
public class Client {
public static void main(String[] args) {
MovieTicket ticket = new MovieTicket();
double originalPrice = 60 ;
double currentPrice;
ticket.setPrice(originalPrice);
System.out.println("原始价格为:"+originalPrice);
System.out.println("=================");
Discount discount = (Discount)XMLUtil.getBean(2);
ticket.setDiscount(discount);
currentPrice=ticket.getPrice();
System.out.println("折扣后价格为:"+currentPrice);
//原始价格为:60.0
//=================
//儿童票:
//折扣后价格为:50.0
//原始价格为:60.0
//=================
//学生票
//折扣后价格为:48.0
//原始价格为:60.0
//=================
//VIP票:
//增加积分
//折扣后价格为:30.0
}
}
24.4JavaSE中的布局
在javaSE中的容器管理布局就是一个策略模式。用户需要对容器对象中成员对象进行布局,在程序客户端决定一个对象如何布局。
24.5策略模式的优缺点
- 优点:用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活的增加新的算法或行为,提供了一种管理产品族的办法的和替换关系的算法
- 缺点:客户端必须知道所有策略,自行决定选用哪一种策略。
24.6策略模式的适用环境
一个系统需要动态的在几种算法中选择一种,避免使用难以维护的多重条件选择语句,不希望客户端去知道复杂的算法和相关的数据结构。
25模板方法模式
25.1模板方法模式的定义
定义一个操作中算法的框架,而将一些步骤延迟到子类中,模板方法模式使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。
举个例子,我们一般在每天吃饭,或者请人吃饭,去饭店没回去干的事的步骤基本都一样,点单,吃东西,买单。
就是每次吃的都不一样而已,有时候丰盛,有时候就喝一杯奶茶。就像一个模板一样,就需要改一些地方就行
25.2模板方法模式的结构
- AbstractClass(抽象类):定义了一系列基本操作,可以是具体的也可以是抽象的,在里面将基本方法进行定义
- ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作,完成子类特定算法的步骤,也可以覆盖父类中的基本操作。
25.3模板方法模式的实现
在抽象类中一般加入钩子方法,让其子类进行非空实现,在实现步骤的时候钩子方法可以和某一些具体步骤进行挂钩,可以在不同条件下执行模板方法中的不同步骤,对某个条件进行判断
比如当我们在最熟悉的登录系统的建立中,当登录成功才会进行下面操作,而且不同的操作显示也有所不同
先写抽象类
public abstract class Account {
public boolean validate(String account,String password){
System.out.println("账号是:"+account);
System.out.println("密码是:"+password);
if(account.equalsIgnoreCase("李杰") && password.equalsIgnoreCase("123456")) {
return true;
}else {
return false;
}
}
public abstract void calculateInterest();
public void display(){
System.out.println("显示利息");
}
public void handle(String account ,String password){
if(!validate(account,password)){
System.out.println("账号或者密码错误");
return;
}else {
calculateInterest();
display();
}
}
}
分别写两个实现
public class CurrentAccount extends Account {
@Override
public void calculateInterest() {
System.out.println("按活期利率计算利息");
}
}
public class SavingAccount extends Account {
@Override
public void calculateInterest() {
System.out.println("按定期利率计算利息");
}
}
然后同样的利用xml配置类进行选取类的实例对象。进行测试
public class Client {
public static void main(String[] args) {
Account account = (Account) XMLUtil.getBean(0);
account.handle("xxxx","123456");
account.handle("李杰","123456");
//账号是:xxxx
//密码是:123456
//账号或者密码错误
//账号是:李杰
//密码是:123456
//按活期利率计算利息
//显示利息
}
}
25.4钩子方法的适用
上个例子中钩子方法时是在模板方法中实现了,但是功能过于单一,拓展性不好,应该将钩子方法定义默认的返回值,子类可以通过不同的需求重写这个方法,实现钩子方法的内容转变,更好的利用钩子方法来实现相应的功能。
25.5模板方法的优缺点
- 优点:在父类中形式化定义一个算法,由子类来实现细节处理,在子类实现详细的细节处理,不会改变算法的处理步骤,提取了库中的公共行为,将公共行为放在父类,不同的行为由子类分别实现,符合单一职责和开闭原则
- 缺点:需要每一个基本方法的不同实现提供一个子类,如果父类可变的方法太多,导致子类数量增加。
25.6模板方法适用环境
一次性实现的一个算法的不变部分,并将可变部分留给子类来实现,各种子类中的公共行为被提取到一个公共父类中以避免代码重复,需要子类来决定某个算法步骤是否执行,实现子类对父类的反向控制。
26访问者模式
26.1访问者模式的定义
表示作用于某个对象结构中各个元素的操作。访问者模式让用户可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
举个例子,当我们医院挂完号之后,得到相应的处方单,去前台,就分成两个操作,一个是要计算价格,另一个就是通过处方单来给你找药。其中单子上的每个药就是元素,算价格的和找药的就是访问者。
26.2访问者模式的结构
- Visitor(抽象访问者):定义访问操作
- ConcreteVisitor(具体访问者):实现具体的访问操作
- Element(抽象元素):一般是抽象类或者接口,声明accept()方法,用于接收访问者操作,通常一个抽象访问者作为参数
- ConcreteElement(具体元素):实现了accept()方法,在方法中调用访问者去访问,以便于对一个元素完成操作
- ObjectStructure(对象结构):对象结构是一个元素的集合,用于存放元素对象,并且为其提供了遍历其内部元素的方法。可以是一个简单的集合对象
26.3访问者模式的实现
实例:在某个公司系统中规定,每周工作时间最少40小时对于正式员工,但是超出部分有加班费,少用40小时扣工资,扣完为止,临时工按没小时的价格算钱,但对与财务部门只看钱,对于人力部门只看时间,只计算工作时间。所以临时工和正式工分别是两个元素,而财务部门和人力部门为两个访问者,还需要一个对象结构把,所以元素装进去以便于访问者访问
先写员工类的抽象类和实现类
public interface Employee {
void accept(Department handler);
}
public class FulltimeEmployee implements Employee {
private String name;
private double weeklyWage;
private int workTime;
public FulltimeEmployee(String name, double weeklyWage, int workTime) {
this.name = name;
this.weeklyWage = weeklyWage;
this.workTime = workTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getWeeklyWage() {
return weeklyWage;
}
public void setWeeklyWage(double weeklyWage) {
this.weeklyWage = weeklyWage;
}
public int getWorkTime() {
return workTime;
}
public void setWorkTime(int workTime) {
this.workTime = workTime;
}
@Override
public void accept(Department handler) {
handler.visit(this);
}
}
public class ParttimeEmployee implements Employee {
private String name;
private double hourWage;
private int workTime;
public ParttimeEmployee(String name, double hourWage, int workTime) {
this.name = name;
this.hourWage = hourWage;
this.workTime = workTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHourWage() {
return hourWage;
}
public void setHourWage(double hourWage) {
this.hourWage = hourWage;
}
public int getWorkTime() {
return workTime;
}
public void setWorkTime(int workTime) {
this.workTime = workTime;
}
@Override
public void accept(Department handler) {
handler.visit(this);
}
}
再写部门的抽象类,和抽象方法,并提供实现类
public abstract class Department {
public abstract void visit(FulltimeEmployee employee);
public abstract void visit(ParttimeEmployee employee);
}
public class FADepartment extends Department {
@Override
public void visit(FulltimeEmployee employee) {
int workTime = employee.getWorkTime();
double weeklyWage = employee.getWeeklyWage();
if(workTime>40){
weeklyWage = weeklyWage+(workTime-40)*100;
}else if(workTime<40){
weeklyWage = weeklyWage-(40-workTime)*80;
if(weeklyWage<0){
weeklyWage=0;
}
}
System.out.println("正式员工"+employee.getName()+"实际工资为:"+weeklyWage+"元");
}
@Override
public void visit(ParttimeEmployee employee) {
int workTime = employee.getWorkTime();
double hourWage = employee.getHourWage();
System.out.println("临时工"+employee.getName()+"实际工资为:"+workTime*hourWage+"元");
}
}
public class HRDepartment extends Department {
@Override
public void visit(FulltimeEmployee employee) {
int workTime = employee.getWorkTime();
System.out.println("正式员工"+employee.getName()+"实际工作时间为:"+workTime+"小时");
if(workTime>40){
System.out.println("正式员工"+employee.getName()+"加班时间为:"+(workTime-40)+"小时");
}else if(workTime<40){
System.out.println("正式员工"+employee.getName()+"请假时间为:"+(40-workTime)+"小时");
}
}
@Override
public void visit(ParttimeEmployee employee) {
int workTime = employee.getWorkTime();
System.out.println("临时工"+employee.getName()+"实际工作时间为:"+workTime+"小时");
}
}
最后在写一个员工列表以便于访问者访问
public class EmployeeList {
private ArrayList<Employee> list = new ArrayList<>();
public void addEmployee(Employee employee){
list.add(employee);
}
public void accept(Department handler){
for(Object o: list){
((Employee)o).accept(handler);
}
}
}
最后进行测试,同样的我们也借助xml的工具类把 类的全路径写入到配置类中,借助工具类调用类的实例
public class Client {
public static void main(String[] args) {
EmployeeList list =new EmployeeList();
Employee fte1,fte2,fte3,pte1,pte2;
fte1=new FulltimeEmployee("张无忌",3200.00,45);
fte2=new FulltimeEmployee("杨过",2000.00,40);
fte3=new FulltimeEmployee("段誉",2400.00,38);
pte1 =new ParttimeEmployee("洪七公",80.00,20);
pte2 =new ParttimeEmployee("郭靖",60.00,18);
list.addEmployee(fte1);
list.addEmployee(fte2);
list.addEmployee(fte3);
list.addEmployee(pte1);
list.addEmployee(pte2);
Department department = (Department) XMLUtil.getBean(0);
list.accept(department);
//正式员工张无忌实际工资为:3700.0元
//正式员工杨过实际工资为:2000.0元
//正式员工段誉实际工资为:2240.0元
//临时工洪七公实际工资为:1600.0元
//临时工郭靖实际工资为:1080.0元
//---------------------------------------
//正式员工张无忌实际工作时间为:45小时
//正式员工张无忌加班时间为:5小
//正式员工杨过实际工作时间为:40小时
//正式员工段誉实际工作时间为:38小时
//正式员工段誉请假时间为:2小时
//临时工洪七公实际工作时间为:20小时
//临时工郭靖实际工作时间为:18小时
}
}
26.4组合模式和访问者模式联合使用。
在访问者模式中的元素对象的集合需要用迭代器来使用,遍历,同时具体元素之间存在整体和部分的关系,有些元素作为容器对象,可以用组合模式来组织元素。
26.5访问者模式的优缺点
- 优点:增加新的访问操作很简单很方便,将有关的访问对象的行为集中到一个访问者对象中去,而不是分散在元素类中,类的职责更加清晰,让用户能不修改现有的层级结构的情况下定义作用于该层次结构的操作。
- 缺点:增加新的元素类很难,可能破坏系统的封装类
26.6访问者模式的适用环境
一个对象结构包含多个类型对象,希望对这些对象实施一些依赖性其具体类型操作,需要对一个对象结构中的对象进行很多不同的且不想关的操作,也不希望增加新的操作的时候修改这些类。