例如,我有两个命名空间,每个命名空间中有三个类。这两组三个类——出于我的目的——名称和属性相同,但每组都存在于自己的命名空间中。
正如您所猜想的那样,此代码是从同一服务的两个实例自动生成的(一个用于生产,另一个用于 UAT)。每个实例可能在同一个版本上,也可能不在同一个版本上,但是我关心的成员总是相同的。
例如:
namespace Domain.Uat //Auto-generated
{
public class LoginResult { }
public class ServiceSession { }
public class Service
{
public ServiceSession Session { get; set; }
public LoginResult Login(string username, string password);
}
}
namespace Domain.Prod //Auto-generated
{
public class LoginResult { }
public class ServiceSession { }
public class Service
{
public ServiceSession Session { get; set; }
public LoginResult Login(string username, string password);
}
}
我有一个类可以登录到服务并做很多其他的事情。我希望能够从 UI 层选择 UAT 或 Prod,因此我在我的 - 在本例中 - Login() 方法中添加了一个名为“isUat”的布尔参数。
我想避免编写两个几乎相同的 Login() 方法。所以我在想这样的事情:
namespace Domain
{
public class DomainClass
{
private object MyService;
private object MyLoginResult;
private object MySession;
public void Login(string username, string password, bool isUat)
{
//New up the service here:
if (isUat)
MyService = new Domain.Uat.Service();
else
MyService = new Domain.Prod.Service();
//Do all the common stuff here
MyLoginResult = MyService.Login(username, password);
MySession = MyService.Session;
}
//(Lots of other methods that use MyService and MySession go here)
}
}
声明类型对象并将其强制转换是愚蠢的,但我认为它说明了我的一般方法。我已经让它使用部分类和接口(“ILoginResult”、“IServiceSession”、“IService”等)以几种不同的方式工作,但感觉很hacky。
这个问题是否有最佳实践模式,或者我是否坚持编写两个 Login() 方法?
更新
我上面提到的部分类和接口方法是这样工作的:
namespace Domain
{
public interface ILoginResult { }
public interface IServiceSession { }
public interface IService
{
public IServiceSession Session { get; set; }
public ILoginResult Login(string username, string password);
}
}
然后我添加一些代码(不是自动生成的)来扩展自动生成的类:
namespace Domain.Uat //Do the same for Domain.Prod
{
public partial class LoginResult : Domain.ILoginResult { }
public partial class ServiceSession : Domain.IServiceSession { }
public partial class Service : Domain.IService { } //Doesn't work, see below
}
然后我像这样修改我的 DomainClass :
namespace Domain
{
public class DomainClass
{
private ILoginResult MyLoginResult;
private IServiceSession MyServiceSession;
private IService MyService;
public void Login(string username, string password, bool isUat)
{
//New up the service here
if (isUat)
MyService = new Domain.Uat.Service();
else
MyService = new Domain.Prod.Service();
//Common stuff here
MyLoginResult = MyService.Login(username, password);
MyServiceSession = MyService.ServiceSession;
}
//More members here
}
}
但是,这不起作用,因为 Domain.Uat.Service 实际上并没有实现 Domain.IService。Domain.IService.ServiceSession 获取设置 IServiceSession,但 Domain.Uat.Service 实际上没有名为 ServiceSession 的 IServiceSession 属性。LoginResult 也是如此。
所以,现在在我的部分课程中,我不得不像这样实现新的具体成员:
namespace Domain.Uat //Do the same for Domain.Prod
{
public partial class LoginResult : Domain.ILoginResult { }
public partial class ServiceSession : Domain.IServiceSession { }
public partial class Service : Domain.IService
{
public Domain.IServiceSession Domain.IService.ServiceSession
{
get { return Session; }
set { Session = (Domain.Uat.ServiceSession)value; }
}
public Domain.ILoginResult Domain.IService.Login(string username, string password)
{
return Login(username, password);
}
}
}
我不喜欢这样,所以我尝试使用泛型来避免具体实现,如下所示:
namespace Domain
{
public interface ILoginResult { }
public interface IServiceSession { }
public interface IService<TLoginResult, TServiceSession>
where TLoginResult : ILoginResult
where TServiceSession : IServiceSession
{
public TServiceSession Session { get; set; }
public TLoginResult Login(string username, string password);
}
}
...这导致我的服务类签名发生变化,如下所示:
namespace Domain.Uat //Do the same for Domain.Prod
{
public partial class LoginResult : Domain.ILoginResult { }
public partial class ServiceSession : Domain.IServiceSession { }
public partial class Service : Domain.IService<LoginResult, ServiceSession> { }
}
所以我避免了具体的实现,但是现在当我在我的 DomainClass 中使用服务时编译器会中断:
namespace Domain
{
public class DomainClass
{
private IService MyService;
private ILoginResult MyLoginResult;
private IServiceSession MySession;
public void Login(string username, string password, bool isUat)
{
//New up the service here:
if (isUat)
MyService = new Domain.Uat.Service(); //Compiler fail, can't cast
else
MyService = new Domain.Prod.Service(); //Compiler fail, can't cast
//Do all the common stuff here
MyLoginResult = MyService.Login(username, password);
MySession = MyService.Session;
}
//(Lots of other methods that use MyService and MySession go here)
}
}
感觉自己挖的坑太深了,退了一步,发到 Stack Overflow 上!
PS 协变和逆变不适用于我的 IService 接口,因为一个成员是一个属性(是的,我需要能够在 Session 上获取和设置)。