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