GRASP软件职责分配原则
GRASP简介
GRASP是General Responsibility Assignment Software Pattern的缩写,是一组为软件对象分配职责的指导原则。GRASP包含9种模式,它们是:
- 创建者Creator
- 信息专家Information Expert
- 低耦合Low Coupling
- 高内聚High Cohesion
- 控制器Controller
- 多态Polymorphism
- 纯虚构Pure Fabrication
- 间接性Indirection
- 防止变异Protected Variation
下面逐一介绍这9种模式。
创建者
创建者模式用于指导创建对象职责的分配。假设有两个类A和B,并且下列条件之一得到满足,那么应当将创建A类实例对象的职责分配给B类。
- B包含A,或者由A聚合而成。
- B记录A。
- B紧密的使用A。
- B具有初始化A的初始化所需要的数据。
创建者模式的优点是可以降低耦合。下面是创建者模式的示例。
// 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;
}
}
// 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;
}
}
信息专家
信息专家模式可以指导一般职责的分配。信息专家模式建议将职责分配给拥有完成职责所需信息的类,这样的类称为信息专家。信息专家可以保护数据的封装性,让数据和行为关联在一起,提高类的内聚性。创建者模式可以看作信息专家模式的特例。
// 类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);
}
}
低耦合
耦合是度量各对象之间相互连接(依赖和感知)程度的。低耦合模式要求在分配职责时,选择耦合程度较低的方案。低耦合让一个类不容易受其他类变化的影响,使得软件更易于修改,更加灵活,降低了变更对软件的影响,同时也使得类更便于理解。
// 耦合度高。
// 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) {}
}
高内聚
内聚描述了一个类内部所有职责的相关性。如果一个类的所有职责都和某个主题相关,就称这个类就是高内聚的。高内聚提高了类的可理解性和可管理性。通常几个高内聚的类它们之间的耦合成都也往往较低。高内聚低耦合让软件在面对需求变化时,整体设计可以保持稳定。
// 低内聚
// 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));
}
}
控制器
控制器模式用于寻找UI层中首先接收和协调系统操作的对象。控制器模式建议将这一职责分配给下列对象:
- 代表全部系统的根对象。
- 运行软件的设备或主要的子系统,它们通常是外观控制器(facade controller)的变体。
- 代表发生操作的用例场景,比如用例或会话控制器。
控制器可以将请求和处理逻辑分离,增加了可复用和可插拔的能力。应用控制器时要避免控制器过于臃肿。一个控制器应当只处理一类任务:要么将分派任务给下级控制器,或要么处理某一类具体的业务逻辑。
// 所有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);
}
}
多态
多态模式用于处理基于类型的分支条件。如果这些分支使用if-else或switch语句实现,当出现新的变化时,往往需要修改大量散落在各处的的if语句,让软件难以维护,也容易出现缺陷。多态模式建议:当相关选择或行为随类型而改变时,应当使用多态操作为变化的行为分配职责。
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");
}
}
纯虚构
如果专家模式无法提供高内聚、低耦合的方案,可以考虑采用纯虚构模式。纯虚构模式会制造一个现实中不存在的事物,让它来承担一族高内聚职责。纯虚构的优点是支持高内聚。缺点是容易被滥用。
public interface FlyBehavior {
void fly();
}
public class FlyWithWings implements FlyBehavior {
public void fly() { /* ... */ }
}
public class FlyNoWay implements FlyBehavior {
public void fly() { /* ... */ }
}
间接性
间接性模式是为了避免多个事物之间直接耦合,解决方法是将职责分配给中介对象,让中介对象作为构建或服务之间的媒介。GoF设计模式中的适配器(Adapter)、外观(Facade)、观察在(Observer)都是间接性模式的特例。
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();
}
}
防止变异
防止变异模式解决的是,如何设计对象、子系统和系统,使得其内部的变化或不稳定性不会对其他元素产生不良影响?方法是预计变化或不稳定之处,分配职责用以在这些变化之外创建稳定接口。
编辑记录
- 2019年12月24日 新建文档。
- 2024年02月10日 调整部分文字和格式。