从问题角度来思考设计模式(2) – 结构编

目录

  1. 生成编
  2. 结构编
  3. 行为编

让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方法,执行foo2foo3方法时会报错。
从上面的代码可以发现TestUserNormalUserfoo1代码内容是一样,返回了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模式,控制对某个对象的访问。如果需要,可以给不同的用户提供不同级别的使用权限。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注