事件
事件是我非常喜欢 .net 的一件事情,因为它允许您声明一个更清晰的接口。您可以有一个 president 类来宣布它需要一个答案,而不将其绑定到回答代理的实现方式,例如:
interface IPresident
{
event Action<QuestionArgs, IPresident> HasQuestion;
void RecieveAnswer(QuestionArgs,Answer);
}
然后在你的科学家类中
partial class Scientist
{
public Scientist(IPresident president)
{
president.HasQuestion += TryToAnswerQuestion;
}
private void TryToAnswerQuestion(QuestionArgs question, IPresident asker)
{
if(CanAnswerQuestion(question))
{
asker.RecieveAnswer(question,GetAnswer(question));
}
}
}
如果一个新的类想要回答总统的问题,他们只需要监听事件信号以表明有需要回答的问题,然后如果他们能够回答,就回答它。 如果科学家想要回答别人的问题,我们只需要实现一个方法来连接他们的事件。
"直接委托调用"
你上面概述的委托方法的问题是它会破坏封装性。 它紧密地耦合了科学家和总统的实现,并使代码变得脆弱。 当你有其他回答问题的人时会发生什么? 在你的例子中,你需要修改你的科学家实现以添加新功能,这被称为“脆弱”的代码,是一件坏事。 这种技术在组合中确实有一些作用,但它很少是最好的选择。
LINQ 的情况不同,因为你没有将委托公开为类/接口的成员。 相反,你将其作为由调用者声明的函数对象使用,让你知道调用者感兴趣的信息。 由于你正在进行“往返”操作,所以封装保持完整。
这让您定义非常清晰而强大的 API。
我们可以采用这种技术来扩展科学家示例,以允许某个人了解我们可以如何回答问题。
partial class Scientist
{
public IEnumerable<QuestionArgs> FindQuestions(Predicate<QuestionArgs> interest, IPresident asker)
{
return this.Questions.Where( x => interest(x) == true && x.IsAuthorizedToAsk(asker))
}
}
partial class President
{
FirePhysicists()
{
foreach(var scientist in scientists)
{
if(scientist.FindQuestions(x => x.Catagory == QuestionCatagory.Physics, this).Count != 0)
{
scientist.Fire();
}
}
}
}
请注意,
FindQuestions
方法使我们不必实现大量其他代码来询问科学家,而这是我们在没有能力传递委托的情况下所需的。虽然这不是您将直接调用委托的唯一情况,但它是最常见的情况之一。