我最近注意到很多注意力都集中在SOLID原则上。这是非常好的事情,因为它是面向对象设计(OOD)和编程的总体基础。对于面向对象语言的开发人员,SOLID原则的知识是编写具有良好质量特征的代码的要求。关于这些规则有很多文章和课程,所以如果你还不了解它们,请尽快学习。
另一方面,关于面向对象编程还有另一个不太为人所知的规则。它被称为GRASP - 一般责任分配软件模式(或原则)。互联网上关于这个主题的材料要少得多,所以我决定把它拉得更近,因为我认为其中描述的原则和SOLID原则一样重要。
免责声明:这篇文章的灵感源自Craig Larman的书:应用UML和模式:面向对象的分析和设计以及迭代开发简介。虽然上一版本是在2004年发布的,但据我所知,这本书仍然是最新的,并且完美地解释了如何使用面向对象语言设计系统。相信我,很难找到关于这个主题的更好的书。它不是关于UML的书,但你可以从中学习UML,因为它也很好解释。每个开发者必须拥有的时间段。
软件职责责任
软件责任是一个非常重要的概念,不仅涉及类,还涉及模块和整个系统。在责任方面进行思考是考虑软件设计的流行方式。我们总是可以提出以下问题:
- - 这个类/模块/组件/系统的责任是什么?
- -它是负责这个实现功能还是负责的那个功能实现?
- - 在这种特定背景下是否违反了单一责任原则?
但是,为了回答这些类型的问题,我们应该问一个更根本的问题:职责责任是什么意思,什么是在软件的情况下的责任?
做和知道
正如Rebecca Wirfs-Brock在对象设计中所提出的:角色,责任和合作书和她的RDD方法的责任是:
履行任务或了解信息的义务
正如我们从这个定义中看到的,我们在这里明确区分了行为(做)和数据(知道)。
对象职责责任被视为:
- a)自己做某事 - 创建对象,处理数据,做一些计算/计算
- b)启动和协调与其他对象的动作
对象的责任可以定义为:
- a)私有和公共对象数据
- b)相关对象引用
- c)它可以派生的东西
我们来看一个例子:
public class Customer :
Entity, // knowing 这属于知道
IAggregateRoot // knowing 继承聚合根属于知道
{
public Guid Id { get; private set; } // knowing
public string Email { get; private set; } // knowing
public string Name { get; private set; } // knowing
private readonly List<Order> _orders; // knowing
private Customer()
{
this._orders = new List<Order>();
}
// doing something itself 这属于做某些事情
public Customer(string email, string name, ICustomerUniquenessChecker customerUniquenessChecker)
{
this.Email = email;
this.Name = name;
// doing - initiate and coordinate actions with other objects
var isUnique = customerUniquenessChecker.IsUnique(this);
if (!isUnique)
{
throw new BusinessRuleValidationException("Customer with this email already exists.");
}
this.AddDomainEvent(new CustomerRegisteredEvent(this));
}
// doing something itself
public void AddOrder(Order order)
{
// doing - initiate and coordinate actions with other objects
if (this._orders.Count(x => x.IsOrderedToday()) >= 2)
{
throw new BusinessRuleValidationException("You cannot order more than 2 orders on the same day");
}
this._orders.Add(order);
this.AddDomainEvent(new OrderAddedEvent(order));
}
// doing something itself
public void ChangeOrder(
Guid orderId,
List<OrderProduct> products,
List<ConversionRate> conversionRates)
{
var order = this._orders.Single(x => x.Id == orderId);
// doing - initiate and coordinate actions with other objects
order.Change(products, conversionRates);
this.AddDomainEvent(new OrderChangedEvent(order));
}
// doing something itself
public void RemoveOrder(Guid orderId)
{
var order = this._orders.Single(x => x.Id == orderId);
// doing - initiate and coordinate actions with other objects
order.Remove();
this.AddDomainEvent(new OrderRemovedEvent(order));
}
// doing something itself
public GetOrdersTotal(Guid orderId)
{
return this._orders.Sum(x => x.Value);
}
}
|
如果您想了解有关软件职责的更多信息并深入了解责任职责驱动设计,您可以直接从Rebecca的Wirfs-Brock书籍或本PDF中阅读。
好的,现在我们知道软件背景下的责任是什么。让我们看看如何使用GRASP分配此职责。
GRASP
GRASP是 General Responsibility Assignment Software Patterns的简称,对象责任的分配是OOD的关键技能之一。每个程序员和设计师都应该熟悉这些模式,更重要的是 - 知道如何在日常工作中应用它们(顺便说一下 - 相同的假设应该适用于SOLID原则)。
这是9种GRASP模式的列表(有时称为原则,但请不要专注于此处的命名):
- 信息专家
- 创建者
- 控制器
- 低耦合
- 高内聚
- 间接
- 多态
- 纯粹制造
- 受保护的变化