GRASP软件职责分配原则

http://tommwq.tech/blog/2020/11/05/189

GRASP是General Responsibility Assignment Software Pattern的缩写,是一组为软件对象分配职责的指导原则。GRASP包含9种模式,它们是:

  • 创建者(Creator)
  • 信息专家(Information Expert)
  • 低耦合(Low Coupling)
  • 高内聚(High Cohesion)
  • 控制器(Controller)
  • 多态(Polymorphism)
  • 纯虚构(Pure Fabrication)
  • 间接性(Indirection)
  • 防止变异(Protected Variation)

下面逐一介绍这9种模式。

1 创建者

创建者模式用于指导创建对象职责的分配。假设有两个类A和B,并且下列条件之一得到满足,那么应当将创建A类实例对象的职责分配给B类。

  • B包含A,或者由A聚合而成。
  • B记录A。
  • B紧密的使用A。
  • B具有初始化A的初始化所需要的数据。

创建者模式的优点是可以降低耦合。下面是创建者模式的示例。Listing 1: 示例:创建者

// MessageBuilder具有初始化Message所需要的数据,因此将创建Message的职责分配给MessageBuilder。
public class MessageBuilder {
    private String senderName;
    private String content;

    public Message build() {
        return new Message(senderName, content);
    }

    public MessageBuilder setSenderName(String aName) {
        senderName = aName;
    }

    public MessageBuilder setContent(String aContent) {
        content = aContent;
    }
}

public class Message {

    private String senderName;
    private String content;

    public Message(String aName, String aContent) {
        senderName = aName;
        content = aContent;
    }

    public String senderName() {
        return senderName;
    }

    public String content() {
        return content;
    }
}

Listing 2: 示例:创建者

// Queue由Entry聚合而成,因此将创建Entry的职责分配给Queue。
public class Queue {

    private ArrayList<Entry> queue = new ArrayList<>();
    private ArrayList<Entry> pool = new ArrayList<>();

    public void post(Payload payload) {
        Entry entry;
        if (pool.isEmpty()) {
            entry = new Entry();
        } else {
            entry = pool.get(0);
            pool.remove(entry);
        }            

        entry.setPayload(payload);
        queue.add(entry);
    }

    public Payload take() {
        if (queue.isEmpty()) {
            return null;
        }

        Entry entry = queue.get(0);
        queue.remove(entry);
        Payload payload = entry.getPayload();
        entry.setPayload(null);
        pool.add(entry);
        return payload;
    }
}

public class Entry {
    private Payload payload = null;

    public void setPayload(Payload aPayload) {
        payload = aPayload;
    }

    public Payload getPayload() {
        return payload;
    }
}

2 信息专家

信息专家模式可以指导一般职责的分配。信息专家模式建议将职责分配给拥有完成职责所需信息的类,这样的类称为信息专家。信息专家可以保护数据的封装性,让数据和行为关联在一起,提高类的内聚性。创建者模式可以看作信息专家模式的特例。Listing 3: 示例:信息专家

// 类User拥有打印用户所需的信息,因此将打印职责赋予User类。
public class User {
    private String name;
    private String email;

    public User(String aName, String aEmail) {
        name = aName;
        email = aEmail;
    }

    public void display() {
        System.out.println("name: %s email: %s", name, email);
    }
}

3 低耦合

耦合是度量各对象之间相互连接(依赖和感知)程度的。低耦合模式要求在分配职责时,选择耦合程度较低的方案。低耦合让一个类不容易受其他类变化的影响,使得软件更易于修改,更加灵活,降低了变更对软件的影响,同时也使得类更便于理解。Listing 4: 耦合

// 耦合度高。
// Client和Logger、FileLogger、ConsoleLogger都产生了耦合。
public interface Logger {}
public class FileLogger implements Logger {}
public class ConsoleLogger implements Logger {}
public class Client {
    public void setFileLogger(FileLogger logger) {}
    public void setConsoleLogger(ConsoleLogger logger) {}
}

// 耦合度低。
// Client、FileLogger、ConsoleLogger之间不再耦合,三者分别和Logger耦合。
public interface Logger {}
public class FileLogger implements Logger {}
public class ConsoleLogger implements Logger {}
public class Client {
    public void addLogger(Logger logger) {}
}

4 高内聚

内聚描述了一个类内部所有职责的相关性。如果一个类的所有职责都和某个主题相关,就称这个类就是高内聚的。高内聚提高了类的可理解性和可管理性。通常几个高内聚的类它们之间的耦合成都也往往较低。高内聚低耦合让软件在面对需求变化时,整体设计可以保持稳定。Listing 5: 内聚

// 低内聚
// Printer类承担了打印和格式化两类职责。
public class Printer {
    public void print(String message) { /* ... */ }
    public void print(String fmt, Object object) {
        print(format(fmt, object));
    }
    public String format(String fmt, Object object) { /* ... */ }
}

// 高内聚
// Printer类只承担打印职责,Formatter类只承担格式化职责。
public class Formatter {
    public String format(String fmt, Object object) { /* ... */ }
}

public class Printer {
    public void print(String message) { /* ... */ }
    public void print(String fmt, Object object) {
        Formatter formatter = new Formatter();
        print(formatter.format(fmt, object));
    }
}

5 控制器

控制器模式用于寻找UI层中首先接收和协调系统操作的对象。控制器模式建议将这一职责分配给下列对象:

  • 代表全部系统的根对象。
  • 运行软件的设备或主要的子系统,它们通常是外观控制器(facade controller)的变体。
  • 代表发生操作的用例场景,比如用例或会话控制器。

控制器可以将请求和处理逻辑分离,增加了可复用和可插拔的能力。应用控制器时要避免控制器过于臃肿。一个控制器应当只处理一类任务:要么将分派任务给下级控制器,或要么处理某一类具体的业务逻辑。Listing 6: 控制器

// 所有Event都发送给Dispatcher,Dispatcher负责分发事件。
public class Dispatcher {
    public void dispatch(Event event) {
        EventProcessor processor = getEventProcessor(event);
        processor.process(event);
    }
}

public interface EventProcessor {
    void process(Event event);
}

public class Forum {
    public void createNewThread() {
        // ...
        Event event = new ThreadCreatedEvent();
        Dispatcher.getInstance().dispatch(event);
    }
}

6 多态

多态模式用于处理基于类型的分支条件。如果这些分支使用if-else或switch语句实现,当出现新的变化时,往往需要修改大量散落在各处的的if语句,让软件难以维护,也容易出现缺陷。多态模式建议:当相关选择或行为随类型而改变时,应当使用多态操作为变化的行为分配职责。Listing 7: 多态

public interface Animal {
    void cry();
}

public Dog implements Animal {
    public void cry() {
        System.out.println("bark");
    }
}

public Cat implements Animal {
    public void cry() {
        System.out.println("mewing");
    }
}

7 纯虚构

如果专家模式无法提供高内聚、低耦合的方案,可以考虑采用纯虚构模式。纯虚构模式会制造一个现实中不存在的事物,让它来承担一族高内聚职责。纯虚构的优点是支持高内聚。缺点是容易被滥用。Listing 8: 纯虚构

public interface FlyBehavior {
    void fly();
}

public class FlyWithWings implements FlyBehavior {
    public void fly() { /* ... */ }
}

public class FlyNoWay implements FlyBehavior {
    public void fly() { /* ... */ }
}

8 间接性

间接性模式是为了避免多个事物之间直接耦合,解决方法是将职责分配给中介对象,让中介对象作为构建或服务之间的媒介。GoF设计模式中的适配器(Adapter)、外观(Facade)、观察在(Observer)都是间接性模式的特例。Listing 9: 间接性

public class ThirdPartyClass {
    public void doSomething() { /* ... */ }
}

public class Adapter {
    private ThirdPartyClass delegate = new ThirdPartyClass();

    public void doSomething() {
        delegate.doSomething();
    }
}

public class Client {
    public void foo() {
        Adapter adapter = new Adapter();
        adapter.doSomething();
    }
}

9 防止变异

防止变异模式解决的是,如何设计对象、子系统和系统,使得其内部的变化或不稳定性不会对其他元素产生不良影响?方法是预计变化或不稳定之处,分配职责用以在这些变化之外创建稳定接口。

猜你喜欢

转载自blog.csdn.net/tq1086/article/details/109516417