Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如果child不';你不知道吗?_C#_Oop_Dependency Injection - Fatal编程技术网

C# 如果child不';你不知道吗?

C# 如果child不';你不知道吗?,c#,oop,dependency-injection,C#,Oop,Dependency Injection,我有一个抽象基类命令,它依赖于ICommandLogger接口: public abstract class Command { public Command(ICommandLogger cmdLogger) { /* ... */ } } 现在所有的继承者都是这样的: public class ConcreteCommand : Command { public ConcreteCommand(CommandLoggersNamespace.ICommandLogger

我有一个抽象基类
命令
,它依赖于
ICommandLogger
接口:

public abstract class Command
{
    public Command(ICommandLogger cmdLogger) { /* ... */ }
}
现在所有的继承者都是这样的:

public class ConcreteCommand : Command
{
     public ConcreteCommand(CommandLoggersNamespace.ICommandLogger cmdLogger)
         : base(cmdLogger)
     {
     }
}
我不喜欢他们被迫知道
ICommandLogger
(他们不使用)


怎么避开这个?或者完全重新设计的原因是什么?

如果他们不使用命令记录器,您可以尝试根本不设置任何命令记录器,如下所示:

public ConcreteCommand()
    : base(null)
{
}
如果这不起作用(抛出一个
异常
),您可以尝试实现一个命令记录器并实例化该记录器:

public ConcreteCommand()
    : base(new MyPhonyCommandLogger())
{
}
如果不希望这些虚假实例出现,请使用静态可用的单个实例:

public ConcreteCommand()
    : base(MyPhonyCommandLogger.Instance)
{
}
我不喜欢他们被迫知道ICommandLogger(他们知道) 不要使用)


他们做得很好;除非您在抽象类中实例化类型为
ICommandLogger
的对象并提供无参数构造函数,否则您当前正在强制继承者了解它。

如果您通过构造函数进行依赖项注入,则无法解决此问题。另一种方法是通过属性设置器进行依赖项注入。就我个人而言,我更喜欢构造函数方法,因为对我来说,它传递的是类需要这个依赖项,而属性注入依赖项传递的是可选依赖项


如果您选择构造函数路线,并且您的基类需要很多依赖项,那么您可以通过这样做来减轻一些痛苦,即基类只需要注入一个参数,而不是许多参数。

另一种看待它的方式是,
ConcreteComand
类确实依赖于
ICommandLogger
。它从命令派生时继承了它

  • 因此,除了让
    ConcreteCommand
    代表其基类接受依赖项之外,您所做的一切都是没有意义的——不要从“
    ConcreteCommand
    有一个
    Command
    ”的角度来考虑它,而是更像“
    ConcreteCommand
    是一个
    命令”

  • 考虑一下,如果您能够以某种方式使基类依赖于
    ICommandLogger
    以某种方式“潜入”构造
    ConcreteCommand
    ,您将如何处理想要覆盖基类日志记录行为的问题

  • 如果您希望能够提供“类似于base”的功能(
    base.Log(“foo”)
    )等),并且您绝对不希望
    ConcreteComand
    了解
    ICommandLogger
    ,那么您可以始终切换到“has a”类型的场景,即
    Command
    只是
    ConcreteCommand
    的一个成员变量(在这种情况下,这是一种愚蠢的理性做法,IMHO!)


在您的设计中有一个问题会给您带来这些麻烦。首先,日志记录是一个问题,您应该防止它污染类。其次,如果让基类实现日志记录,那么将在日志记录基类上添加的下一个横切关注点是什么。当您开始添加另一个横切关注点时n、 基类将违反。您的基类最终将成长为一个具有大量依赖项的大型不可管理类,并且有很多更改的原因

相反,尝试添加日志作为装饰器。但是,您的设计会阻止您有效地这样做,因为您可能有几十个具体的命令,它们都需要自己的装饰器。但是,您的设计的核心问题是,您混合了数据和行为。让该命令只是一个包含一些数据的类a(DTO)和extra将命令逻辑添加到它自己的类中;让我们称之为命令处理程序

除此之外,让命令处理程序实现此接口:

public abstract class Command
{
    public Command(ICommandLogger cmdLogger) { /* ... */ }
}
公共接口ICommandHandler
{
无效句柄(TCommand命令);
}
这将如下所示:

public ConcreteCommand()
    : base(null)
{
}
公共类MoveCustomerCommand
{
公共Guid客户ID;
公共广播;
}
公共类MoveCustomerCommandHandler:ICommandHandler
{
公共无效句柄(MoveCustomerCommand命令)
{
//这里的行为。
}
}
此设计的有趣之处在于,由于所有业务逻辑现在都隐藏在一个逻辑后面,并且此接口是通用的,因此使用decorator包装处理程序变得非常容易。例如,日志decorator:

公共类日志CommandHandlerDecorator
:ICommandHandler
{
私有只读ICommandHandler装饰对象;
公共日志CommandHandlerDecorator(
ICommandHandler(用户、ILog日志)
{
this.decoree=decoree;
this.log=log;
}
公共无效句柄(TCommand命令)
{
this.log.log(“正在执行”+typeof(TCommand).Name+”:“+
序列化(命令));
尝试
{
this.decoree.Handle(命令);
}
捕获(例外情况除外)
{
this.log.log(ex);
投掷;
}
}
}
由于此
LoggingCommandHandlerDecorator
是通用的,因此它可以包装在任何
ICommandHandler
上。这允许使用者依赖某些
ICommandHandler
(例如
ICommandHandler
)您可以向所有业务逻辑添加横切关注点,而无需更改一行代码

在大多数情况下,这将完全消除使用基类的需要


你可以阅读更多关于这种类型的设计。

为什么命令知道抽象ICommandLogger会成为一个问题…?你可以回到属性注入。但是构造函数注入更清晰,因此,更好的选择是IMHO。但是你的
ConcreteCommand
确实知道记录器。只是行为是继承的。这只是一个感知的问题。司机知道他们的车辆的依赖性,这听起来会不会奇怪