鉴于以下课程:
public interface ISession {}
public class Session : ISession
{
internal Uri ServerUri { get; private set; } // not in interface obviously
}
现在我在同一个项目中有另一个类,它取决于Session
:
public interface IDataClass {}
internal DataClass : IDataClass
{
private readonly Session _session;
public DataClass(ISession session)
{
_session = (Session) session;
}
// some more methods that access the internal property _session.ServerUri
// and call methods on the session object
// ...
}
所以实际上Session类非常复杂。对于单元测试,DataClass
我将构造函数参数设为ISession
,因此我能够模拟依赖项并验证ISession
模拟上的某些方法调用。
但是,DataClass
中的某些方法必须访问_session.ServerUri
属性,因此该字段必须是Session
类型,而不是ISession
。
我还可以创建ISession
类型的字段,并在每次访问内部属性时强制转换字段,这将使得在此特定位置更清楚为什么甚至需要具体的internal
类。
每当我以上述方式实现强制转换时,我想知道如果使用更好的代码设计,可以避免使用强制转换。我经常找到一种更好的方法,摆脱演员。
但是上面的例子怎么样?这是好的代码设计吗?还是有更干净的方式?我只是觉得从构造函数初始化字段上面的方式很奇怪。
答案 0 :(得分:5)
老实说,我认为你现有的系统并不是一个好主意。
你的构造函数声明“只要它实现了ISession
”我会接受任何东西,但那不是真的是真的 - 调用代码可以在它们获得时发现它很难尝试创建InvalidCastException
实例时DataClass
。
所以你真的应该在ServerUri
或继承自ISession
的接口中声明ISession
- 但在任何一种情况下,你都需要声明(并且只在DataClass
答案 1 :(得分:4)
你是对的,像这样的投射很容易出错,因为编译器没有简单的方法来告诉它会失败。
但是,看起来问题的根源是缺少对ServerUri
属性的访问权限,因此您添加了强制转换作为变通方法。如果是这种情况,您应该能够两种方式 - 保持接口,并且可以访问属性,如果您可以添加新接口:
public interface ISessionWithServerUri : ISession { // Come up with a better name
Uri ServerUri {get;}
}
现在,您的Session
课程可以实施ISessionWithServerUri
,允许您将_session
类的声明从Session
类更改为ISessionWithServerUri
界面:
internal DataClass : IDataClass
{
private readonly ISessionWithServerUri _session;
public DataClass(ISessionWithServerUri session)
{
_session = session; // Look, no cast!
}
// some more methods that access the internal property _session.ServerUri
// and call methods on the session object
// ...
}
答案 2 :(得分:3)
由于DataClass
取决于Session
,因此它应该在其构造函数中声明它。该类的任何客户端都应该能够从构造函数中定义的类型/契约中创建有效的实例。
如果你想依赖一个界面,那么你应该用你需要的成员创建一个新界面。
public interface IUriSession : ISession
{
Uri ServerUri { get; }
}
并将构造函数更改为
public DataClass(IUriSession session) { ... }
答案 3 :(得分:2)
我总是试图避免像构造函数中那样的显式类型转换。我假设你正在使用IoC容器来解决你的依赖关系,如果具体的类型映射曾经改变为实现该接口的另一个类(并且没有内部属性),这显然会破坏。它也有点破坏了该类中使用的依赖注入的目的。
您是否可以更改DataClass的构造函数以注入其他类型?如果是这样,您可以创建一个实现ISession并且还包含ServerUri属性的接口,创建一个实现新接口的类,然后将该类型注入构造函数(接口)。