目录
让数据和处理逻辑分离
- 改造前
public class FooAmusementPark {
private FooZoo zoo;
private FooAquarium aquarium;
public void enjoy(FooFamily family) {
zoo.enjoy();
}
public void enjoy(FooCouple couple) {
aquarium.enjoy();
}
}
当Family
,Couple
以外类型的用户增加时,FooAmusementPark类则必须扩充
- 改造后
Visitor
public class FooAmusementPark {
private FooZoo zoo;
private FooAquarium aquarium;
public void accept(FooVisitor visitor) {
visitor.visit(this);
}
}
public class FooFamily extends FooVisitor {
@Override
public void visit(FooAmusementPark park) {
park.getZoo().enjoy();
}
}
public class FooCouple extends FooVisitor {
@Override
public void visit(FooAmusementPark park) {
park.getAquarium().enjoy();
}
}
改造后,Family
,Couple
以外类型的用户增加时,AmusementPark
不需要更改,通过其中的visit
方法就能满足需求。
当数据结构不经常变化的,是使用Visitor
模式的考虑点。
需求多变时,如何分离处理逻辑
- 改造前
public class FooController {
private FooLatLngToPlaceAPI api;
public String getFormattedAddress(double latitude, double longitude) {
FooPlace place = api.getPlace(latitude, longitude);
return place.getPostalCode()
+ System.lineSeparator()
+ place.getProvince()
+ " "
+ place.getCity();
}
}
假设返回的数据邮编 换行 省 市
格式。下一次迭代可能又换成邮编 省-市
或者其它。当多个地方返回地址数据都希望保持同一的格式时,一个个去拼装字符串就显得有些繁琐了。
- 改造后
Strategy
public class FooController {
private FooLatLngToPlaceAPI api;
private FooAddressFormatter formatter = new FooAddressFormatter();
public String getFormattedAddress(double latitude, double longitude) {
FooPlace place = api.getPlace(latitude, longitude);
return formatter.format(place);
}
}
public class FooAddressFormatter {
public String format(FooPlace place) {
return place.getPostalCode()
+ System.lineSeparator()
+ place.getProvince()
+ " "
+ place.getCity();
}
}
将拼装返回数据的逻辑单独封装成一个类。
当需求变化时,也只需要改动这个类的逻辑即可,而又能保持全局格式统一。
当不想创建一个新类时,也可以这样子写
public class FooController {
private FooLatLngToPlaceAPI api;
private static final Function<FooPlace, String> FORMAT_ADDRESS =
place -> place.getPostalCode()
+ System.lineSeparator()
+ place.getPrefecture()
+ " "
+ place.getCity();
public String getFormattedAddress(double latitude, double longitude) {
FooPlace place = api.getPlace(latitude, longitude);
return FORMAT_ADDRESS.apply(place);
}
}
根据条件区分近似的处理逻辑
- 改造前
public String getFormattedText(String text, FormatType type) {
switch (type) {
case BOLD:
return "**" + text + "**";
case ITALIC:
return "*" + text + "*";
default:
return text;
}
}
当type
值变多时,switch里的条件判断及代码块就变得越来越多了,不利代码维护。
- 改造后
Strategy + Factory
public class FooFormatterFactory {
private FooFormatterFactory() {}
public static FooFormatter create(FormatType type) {
switch (type) {
case BOLD:
return new FooBoldFormatter();
case ITALIC:
return new FooItalicFormatter();
default:
return new FooFormatter() {
@Override
public String format(String text) {
return text;
}
}
}
}
}
public class FooBoldFormatter extends FooFormatter {
@Override
public String format(String text) {
return "**" + text + "**";
}
}
public class FooItalicFormatter extends FooFormatter {
@Override
public String format(String text) {
return "*" + text + "*";
}
}
public String getFormattedText(String text, FormatType type) {
return FooFormatterFactory.create(type).format(text);
}
改造后,当type
值变多时,getFormattedText
方法内容则不受影响。
通过Strategy
和Factory
模式结合使用的例子挺多的。
当不想创建新类的时,可以参考下面代码,用函数式接口实现
public class FooFormatterFactory {
private FooFormatterFactory() {}
public static UnaryOperator<String> create(FormatType type) {
switch (type) {
case BOLD:
return text -> "**" + text + "**";
case ITALIC:
return text -> "*" + text + "*";
default:
return text -> text;
}
}
}
public String getFormattedText(String text, FormatType type) {
return FooFormatterFactory.create(type).apply(text);
}
如何利用上一次处理的结果
- 改造前
public class FooController {
private int nextId;
public FooResponse get(FooRequest request) {
FooResponse response = getResponse(request);
nextId = response.getNextId();
return response;
}
public FooResponse getNext() {
FooRequest request = new FooRequest(nextId);
return get(request);
}
private FooResponse getResponse(FooRequest request) {
// 处理
return // 结果
}
}
在getNext()
方法中把包含上一次的结果的nextId做为参数生成request
实例。当request
生成时所需要的参数的数量或类型改变时,FooController
中所有的值,逻辑等都需要变更。
- 改造后
Memento
public class FooMemento {
private int nextId;
public void update(FooResponse response) {
this.nextId = response.getNextId();
}
public FooRequest createNextRequest() {
return new FooRequest(nextId);
}
}
public class FooController {
private FooMemento memento = new FooMemento();
public FooResponse get(FooRequest request) {
FooResponse response = getResponse(request);
memento.update(response);
return response;
}
public FooResponse getNext() {
return get(memento.createNextRequest());
}
private FooResponse getResponse(FooRequest request) {
// 处理
return // 结果
}
}
通过Memento
模式来记忆上次数据。
当新request
生成时所需要的参数变化时,只需要在FooMemento
中做修改。
Memento
还经常用到单机游戏里,如小时候玩的存档型游戏,存档就是记录某进度下的角色们的各种状态,位置,关卡等信息。当读取存档时,其实也就是读取里面的状态数据,将程序恢复到该时刻。