目录
让API返回信息适配多样性
- 改造前
FooAPI fooAPI = new FooAPI(lat, lng);
FooPlace place = new FooPlace();
place.setAddress(fooAPI.getPostalCode() + " " + fooAPI.getAddress()); // 邮编 **市**区
place.setStation(
new StringJoiner(",")
.add(fooAPI.getStation1())
.add(fooAPI.getStation2())
.add(fooAPI.getStation3())
.toString()); //A车站,B车站,C车站
上面代码是API返回信息整形用例。这里只使用了FooAPI
,未来如果返回信息的整形方式不变的话,倒也没有什么问题。但从易维护的角度思考,这称不上一个好的设计。
- 改造后
Adapter
public class FooAPIAdapter {
private final FooAPI fooAPI;
public FooAPIAdapter(double latitude, double longitude) {
this.fooAPI = new FooAPI(latitude, longitude);
}
public String getAddress() {
return fooAPI.getPostalCode() + " " + fooAPI.getAddress();
}
public String getStation() {
return new StringJoiner(",")
.add(fooAPI.getStation1())
.add(fooAPI.getStation2())
.add(fooAPI.getStation3())
.toString();
}
}
FooAPIAdapter fooAPI = new FooAPIAdapter(lat, lng);
FooPlace place = new FooPlace();
place.setAddress(fooAPI.getAddress());
place.setStation(fooAPI.getStation());
在这里引入了Adapter(适配器)
的概念。当返回信息的整形方式改变的时候,只要修改对应的Adapter
类就可以,而对调用方不会产生代码改动。引入设计模式的初衷都为了,业务上的解耦,让代码更聚焦。
让API调用方不需要考虑操作顺序
- 改造前
public class FooSorter {
private List<FooStudent> students = new List<>();
public void add(FooStudent student) {
students.add(student);
}
public void sort() {
students.sort(
Comparator.comparingInt(
student -> student.getChineseScore()
+ student.getMathScore()
+ student.getEnglishScore())
.reversed());
}
public List<FooStudent> getResult() {
return students;
}
}
FooSorter sorter = new FooSorter();
sorter.add(student1);
sorter.add(student2);
sorter.add(student3);
sorter.sort();
sorter.getResult();
需要遵循实例生成 -> add() -> sort() -> getResult()
流程来调用,调用方如果不知道流程则调用失败,故这样的设计并不好。
- 改造后
Facade
public class FooSorter {
private List<FooStudent> students = new List<>();
private FooSorter() {}
public static List<FooStudent> sort(FooStudent... students) {
for (FooStudent student : students)
add(student);
sort();
return getResult();
}
private void add(FooStudent student) {
students.add(student);
}
private void sort() {
students.sort(
Comparator.comparingInt(
student -> student.getChineseScore()
+ student.getMathScore()
+ student.getEnglishScore())
.reversed());
}
private List<FooStudent> getResult() {
return students;
}
}
FooSorter.sort(student1, student2, student3);
调用方不需要知道排序逻辑,且调用代码行数也缩减1行。涉及多个类按顺序执行复杂的处理时,可以考虑使用Facade
模式。常见的有,手机的一键静音模式,该模式包括了音量设置,振动器设置等。玩魔兽世界的玩家肯定更熟悉,就是传说中的一键宏,惩戒骑用脸滚键盘的回忆有木有。
让同级类的结果组合起来
- 改造前
public class FooPosition {
private int x;
private int y;
public void moveAs(FooMove move) {
move.move(this);
}
}
public abstract class FooMove {
private final int addition;
public FooMove(int addition) {
this.addition = addition;
}
public abstract void move(FooPosition position);
}
public class FooMoveHorizontal extends FooMove {
public FooMoveHorizontal(int addition) {
super(addition);
}
@Override
public void move(FooPosition position) {
position.setX(position.getX() + addition);
}
}
public class FooMoveVertical extends FooMove {
public FooMoveVertical(int addition) {
super(addition);
}
@Override
public void move(FooPosition position) {
position.setY(position.getY() + addition);
}
}
FooMove moveHorizontal = new FooMoveHorizontal(x);
FooMove moveVertical = new FooMoveVertical(y);
FooPosition position = new FooPosition();
position.moveAs(moveHorizontal);
position.moveAs(moveVertical);
上面的代码本身设计没有什么问题,但每次都只能往一个方向,要么水平要么垂直方向移动。如何实现一次调用能达到同时操作x,y呢?
- 改造后
Decorator
public class FooPosition {
private int x;
private int y;
public void moveAs(FooMove move) {
move.move(this);
}
}
public abstract class FooMove {
private final int addition;
private final FooMove move;
public FooMove(int addition) {
this.addition = addition;
}
public FooMove(int addition, FooMove move) {
this.addition = addition;
this.move = move;
}
public abstract void move(FooPosition position);
}
public class FooMoveHorizontal extends FooMove {
public FooMoveHorizontal(int addition) {
super(addition);
}
public FooMoveHorizontal(int addition, FooMove move) {
super(addition, move);
}
@Override
public void move(FooPosition position) {
if (move != null)
move.move(position);
position.setX(position.getX() + addition);
}
}
public class FooMoveVertical extends FooMove {
public FooMoveVertical(int addition) {
super(addition);
}
public FooMoveVertical(int addition, FooMove move) {
super(addition, move);
}
@Override
public void move(FooPosition position) {
if (move != null)
move.move(position);
position.setY(position.getY() + addition);
}
}
FooMove move = new FooMoveHorizontal(x, new FooMoveVertical(y));
FooPosition position = new FooPosition();
position.moveAs(move);
x,y移动的距离汇总到move
这个实例中。且利用这个设计,当移动结束时,还想再水平移动时只需要简单的new FooMoveHorizontal(x, move)
就可以。
有兴趣的朋友可以思考下左左右右上上下下
如何用类来生成?
根据条件不同控制权限
- 改造前
public class FooTestUser extends FooUser {
@Override
public void foo1() {
// NormalUser foo1
}
@Override
public void foo2() {
throw new RuntimeException("this operation is not permitted.");
}
@Override
public void foo3() {
throw new RuntimeException("this operation is not permitted.");
}
}
public class FooNormalUser extends FooUser {
@Override
public void foo1() {
// NormalUser foo1
}
@Override
public void foo2() {
// NormalUser foo2
}
@Override
public void foo3() {
throw new RuntimeException("this operation is not permitted.");
}
}
public class FooSuperUser extends FooUser {
@Override
public void foo1() {
// SuperUser foo1
}
@Override
public void foo2() {
// SuperUser foo2
}
@Override
public void foo3() {
// foo3
}
}
TestUser
只能执行NormalUser
类中的foo1
方法,执行foo2
或foo3
方法时会报错。
从上面的代码可以发现TestUser
和NormalUser
的foo1
代码内容是一样,返回了dry(Don’t repeat yourself)原则。
- 改造后
Proxy
public class FooTestUser extends FooUser {
private FooUser normalUser = new FooNormalUser();
@Override
public void foo1() {
normalUser.foo1();
}
@Override
public void foo2() {
throw new RuntimeException("this operation is not permitted.");
}
@Override
public void foo3() {
throw new RuntimeException("this operation is not permitted.");
}
}
NormalUser和SuperUser类保持不变
通过Proxy
模式,控制对某个对象的访问。如果需要,可以给不同的用户提供不同级别的使用权限。