让我们从单一职责原则(SRP) 的实际含义开始:
一个类应该只有一个改变的理由。
这实际上意味着每个对象(类)都应该有一个单一的职责,如果一个类有多个职责,这些职责就会耦合并且不能独立执行,即在一个特定的实现中,一个对象的更改可能会影响甚至破坏另一个。
必须阅读的是源本身(“敏捷软件开发、原则、模式和实践”中的 pdf 章节):单一职责原则
话虽如此,您应该设计您的课程,以便理想情况下它们只做一件事并做好一件事。
首先考虑一下您拥有哪些“实体”,在您的示例中我可以看到User
以及Channel
它们之间进行通信的媒介(“消息”)。这些实体彼此之间有一定的关系:
这也自然会导致执行以下功能列表:
- 用户可以请求加入频道。
- 用户可以向他加入的频道发送消息
- 用户可以离开频道
- 频道可以拒绝或允许用户的加入请求
- 频道可以踢用户
- 一个频道可以向频道内的所有用户广播一条消息
- 频道可以向频道中的个人用户发送问候消息
SRP 是一个重要的概念,但不应单独存在——对您的设计同样重要的是依赖倒置原则(DIP)。要将其合并到设计中,请记住,您的User
,Message
和Channel
实体的特定实现应该依赖于抽象或接口,而不是特定的具体实现。出于这个原因,我们从设计接口而不是具体类开始:
public interface ICredentials {}
public interface IMessage
{
//properties
string Text {get;set;}
DateTime TimeStamp { get; set; }
IChannel Channel { get; set; }
}
public interface IChannel
{
//properties
ReadOnlyCollection<IUser> Users {get;}
ReadOnlyCollection<IMessage> MessageHistory { get; }
//abilities
bool Add(IUser user);
void Remove(IUser user);
void BroadcastMessage(IMessage message);
void UnicastMessage(IMessage message);
}
public interface IUser
{
string Name {get;}
ICredentials Credentials { get; }
bool Add(IChannel channel);
void Remove(IChannel channel);
void ReceiveMessage(IMessage message);
void SendMessage(IMessage message);
}
该列表没有告诉我们的是执行这些功能的原因。我们最好将“为什么”(用户管理和控制)的责任放在一个单独的实体中——这样,如果“为什么”发生变化User
,Channel
实体就不必改变。我们可以在这里利用策略模式和 DI,并且可以有任何具体的实现IChannel
依赖于一个IUserControl
给我们“为什么”的实体。
public interface IUserControl
{
bool ShouldUserBeKicked(IUser user, IChannel channel);
bool MayUserJoin(IUser user, IChannel channel);
}
public class Channel : IChannel
{
private IUserControl _userControl;
public Channel(IUserControl userControl)
{
_userControl = userControl;
}
public bool Add(IUser user)
{
if (!_userControl.MayUserJoin(user, this))
return false;
//..
}
//..
}
您会看到,在上面的设计中,SRP 甚至还没有接近完美,即 anIChannel
仍然依赖于抽象IUser
和IMessage
.
最后,人们应该争取一种灵活、松散耦合的设计,但总要做出权衡,灰色区域也取决于您希望应用程序在哪里改变。
在我看来,极端的SRP会导致非常灵活但又碎片化和复杂的代码,这些代码可能不像更简单但耦合更紧密的代码那样容易理解。
事实上,如果总是期望两个职责同时发生变化,那么您可能不应该将它们分成不同的类,因为这会导致,引用 Martin 的话,产生“不必要的复杂性”的味道。永远不变的职责也是如此——行为是不变的,没有必要拆分它。
这里的主要思想是你应该做出判断,你看到责任/行为将来可能独立改变,哪些行为相互依赖并且总是同时改变(“绑在臀部”)以及哪些行为一开始就永远不会改变。