たとえば、各名前空間に 3 つのクラスを持つ 2 つの名前空間があります。これらの 3 つのクラスの 2 つのセットは、私の目的では、名前とプロパティが同じですが、各セットは独自の名前空間に存在します。
ご想像のとおり、このコードは同じサービスの 2 つのインスタンス (1 つは運用用、もう 1 つは 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 を選択できるようにしたいので、"isUat" という bool 引数を -- この場合は -- Login() メソッドに追加しました。
ほぼ同一の Login() メソッドを 2 つ記述することは避けたいと考えています。そこで、次のようなことを考えています。
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」など) を使用して、いくつかの異なる方法で動作させましたが、ハッキーに感じます。
この問題のベスト プラクティス パターンはありますか、それとも 2 つの 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);
}
}
...これにより、 Service クラスの署名が次のように変更されます。
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 で Service を使用するとコンパイラが壊れます。
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 Covariance と contravariance は、1 つのメンバーがプロパティであるため、私の IService インターフェイスでは機能しません (はい、Session で取得と設定の両方ができる必要があります)。