0

例如,我有两个命名空间,每个命名空间中有三个类。这两组三个类——出于我的目的——名称和属性相同,但每组都存在于自己的命名空间中。

正如您所猜想的那样,此代码是从同一服务的两个实例自动生成的(一个用于生产,另一个用于 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 上获取和设置)。

4

2 回答 2

1

如果您无法控制生成代码的接口,则可以为从公共接口/抽象类继承的每个服务创建一个包装器。

public class WrappedUatService : IService {
   private Domain.Uat.Service _service;

   public override ILoginResult Login() { 
       ILoginResult result = new WrappedLoginResult(_service.Login());
       ...
   }
}

public class WrappedProdService : IService {
   private Domain.Prod.Service _service;

   public override ILoginResult Login() { 
       ILoginResult result = new WrappedLoginResult(_service.Login());
       ...
   }
}

这是相当多的额外代码,但是您不能仅仅因为它们恰好具有具有相同签名的方法而将单独的类视为等价的。

于 2013-09-19T01:16:42.400 回答
1

我不认为为每个类使用一个接口是 hacky。然后改成这样。

private ILoginResult MyLoginResult

然后为 domainclass 创建一个构造函数并传入 uat 并在构造函数中实例化您的属性。

那么登录方法或任何其他方法都不需要 if 语句

于 2013-09-19T01:10:53.100 回答