访问者模式是用于访问复杂数据结构的元素,对不同的元素执行不同的操作。策略模式是对于具有多种实现的算法,在运行过程中可动态选择使用哪种具体的实现。状态模式是用于具有不同状态的对象,状态之间可以转换,且不同状态下对象的行为不同,客户端可以不必考虑其状态及转换,对所有的状态都可以执行同一的操作。
1 访问者模式
需求:需要对一个复杂数据结构进行操作,根据其不同的元素类型执行不同的操作。
1.1 访问者模式介绍
将数据结构与数据操作分离,通过定义一个访问者对象,来实现对数据结构中各个元素的访问和处理,从而达到解耦和灵活操作的目的。
图 访问者模式 UML
1.1.1 双分派
Java 是一种支持双分派的单分派语言。
|-----|--------------------------------------------|
| 单分派 | 调用哪个对象(多态)的方法,在运行期确定,调用对象的哪个方法,在编译期确定。 |
| 双分派 | 调用哪个对象(多态)的方法,在运行期确定,调用对象的哪个方法,在运行期确定。 |
图 单分派与双分派
访问者模式中的元素对象中的accept方法就是双分派。
public class VisitorPattern {
public static void main(String[] args) {
Element[] elements = {new MobilePhone(),new Ipad(),new Computer()};
Visitor[] visitors = {new Student(),new Programmer()};
for (Visitor visitor : visitors) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
private interface Element {
void accept(Visitor visitor);
}
private static class MobilePhone implements Element{
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void makeCall() {
System.out.println("打电话");
}
public void wechat() {
System.out.println("微信聊天");
}
public void playGame() {
System.out.println("玩王者荣耀");
}
}
private static class Ipad implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void watchTv() {
System.out.println("刷剧");
}
public void playGame() {
System.out.println("玩王者荣耀,大屏更爽");
}
}
private static class Computer implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void playGame() {
System.out.println("玩电脑游戏:使命召唤");
}
public void work() {
System.out.println("用于工作");
}
}
private interface Visitor {
void visit(MobilePhone mobilePhone);
void visit(Ipad ipad);
void visit(Computer computer);
}
private static class Programmer implements Visitor {
@Override
public void visit(MobilePhone mobilePhone) {
mobilePhone.makeCall();
}
@Override
public void visit(Ipad ipad) {
ipad.playGame();
}
@Override
public void visit(Computer computer) {
computer.work();
}
}
private static class Student implements Visitor {
@Override
public void visit(MobilePhone mobilePhone) {
mobilePhone.wechat();
}
@Override
public void visit(Ipad ipad) {
ipad.watchTv();
}
@Override
public void visit(Computer computer) {
computer.playGame();
}
}
}
1.2 优缺点
优点:
- 将数据结构与数据操作分离,当需要添加新的操作时,只添加新的子类即可,符合开闭原则。
- 将对元素的操作集中到一个类中,而不是分散在元素的子类中,使得职责更加明确,代码更加清晰,符合单一职责原则。
缺点:
- 不适合结构不稳定的数据结构,当新增或删除元素时,需要修改访问者类,不符合开闭原则。
- 如果访问者需要访问元素的内部信息,可能会破坏封装性。
2 策略模式
需求:一个算法有多种实现方式,或一个对象有很多的行为,这些行为需要使用多重条件选择语句来实现。期望把这些行为转移到具体的策略类中,避免使用难以维护的多重语句。
2.1 策略模式介绍
定义一个算法接口,然后将其不同的实现封装到具体的策略类中,并让它们可以相互替换。
图 策略模式 UML
public class StrategyPattern {
public static void main(String[] args) {
SortAlgorithm[] sortAlgorithms = {new BubbleSortAlgorithm(),new SelectionSortAlgorithm()};
Integer[][] array = {{34,23,2,4,6,44,11,53,221,123},{3,66,27,212,45,565,11,44,33,12,465,55,22,14,56}};
for (SortAlgorithm sortAlgorithm : sortAlgorithms) {
for (Integer[] arr : array) {
sortAlgorithm.sort(arr);
}
System.out.println();
}
}
private static abstract class SortAlgorithm {
public void sort(Integer[] array) { // 升序
if (array != null && array.length > 1) {
array = Arrays.copyOf(array,array.length);
opera(array);
System.out.println(Arrays.asList(array));
}
}
protected abstract void opera(Integer[] array);
}
/**
* 冒泡排序
*/
private static class BubbleSortAlgorithm extends SortAlgorithm{
@Override
protected void opera(Integer[] array) {
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] > array[j]) {
int temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}
}
}
/**
* 选择排序
*/
private static class SelectionSortAlgorithm extends SortAlgorithm {
@Override
protected void opera(Integer[] array) {
for (int i = 0; i < array.length; i++) {
int minPos = i;
for (int j = i+1; j <array.length; j++) {
if (array[minPos] > array[j]) {
minPos = j;
}
}
int temp = array[i];
array[i] = array[minPos];
array[minPos] = temp;
}
}
}
}
2.2 优缺点
优点:
- 符合开闭原则,增加新的算法或行为时只需要添加子类即可。
- 避免了多重条件选择语句。
缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪个策略类。
- 会增加类的数量。
3 状态模式
需求:系统中某个对象存在多个状态,这些状态之间可以进行转换,且对象在不同状态下的行为不同。
3.1 状态模式介绍
将一个对象的状态从该对象中分离出来,封装到专门的状态类中。对于客户端而言,无须关心对象的状态及转换,无论对象处于哪种状态,客户端都可以一致性地处理。
图 状态模式 UML
public class StatePattern {
public static void main(String[] args) {
BackCard backCard = new BackCard();
backCard.save(100);
backCard.withdraw(1000);
backCard.withdraw(600);
backCard.withdraw(400);
backCard.withdraw(100);
}
private static class BackCard {
private final static State[] stateList = {new NormalState(),new Overdraft(),new LimitState()};
private State state = stateList[0];
private double balance = 0;
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void save(double money) {
System.out.println("存钱操作:" + money);
this.balance += money;
changeState();
}
public void withdraw(double money) {
System.out.println("取钱操作:" + money);
try {
state.withdraw(this,money);
} catch (RuntimeException e) {
System.out.println("取钱失败:" + e.getMessage());
}
changeState();
}
private void changeState() {
System.out.println("-----余额:" + balance + "-----");
if (balance >= 0) state = stateList[0];
else if (balance >= -1000) state = stateList[1];
else state = stateList[2];
}
}
private interface State {
void withdraw(BackCard backCard,double money);
}
private static class NormalState implements State {
@Override
public void withdraw(BackCard backCard,double money) {
backCard.setBalance(backCard.getBalance() - money);
}
}
private static class Overdraft implements State {
@Override
public void withdraw(BackCard backCard,double money) {
if (money > 500) {
throw new RuntimeException("卡被透支,取钱不能超过500元");
}
backCard.setBalance(backCard.getBalance() - money);
}
}
private static class LimitState implements State {
@Override
public void withdraw(BackCard backCard,double money) {
throw new RuntimeException("卡被限制,不能取钱");
}
}
}
3.2 优缺点
优点:
- 客户端可以不必关心对象的状态及转换,对所有状态都可进行同一操作。
- 将与具体状态相关的行为都封装在一个类中,符合单一职责原则,更容易扩展及维护。
缺点:
- 状态转换无论是在环境类还是状态类中,当增加新的状态或修改转换逻辑时,都需要修改转换代码,不符合开闭原则。
- 增加了类的个数。