以下のようなインターフェイス パターンで読み取りアクセスと書き込みアクセスを分離したいとします。
namespace accesspattern
{
namespace ReadOnly
{
public interface IA { double get_a(); }
}
namespace Writable
{
public interface IA : ReadOnly.IA { void set_a(double value); }
}
}
これは簡単に実装できます。
namespace accesspattern
{
namespace ReadOnly
{
public class A : IA
{
protected double a;
public double get_a() { return a; }
}
}
namespace Writable
{
public class A : ReadOnly.A, IA
{
public void set_a(double value) { base.a = value; }
}
}
}
A から継承する別のクラスが必要であり、そのためのインターフェースを定義するとします。
namespace accesspattern
{
namespace ReadOnly
{
public interface IB : ReadOnly.IA { int get_b(); }
}
namespace Writable
{
public interface IB : ReadOnly.IB, Writable.IA { void set_b(int value); }
}
}
これを実装するのはそれほど簡単ではありません。コードの繰り返しを避けるために、Writable.B は Writable.A と ReadOnly.B の 2 つの基本クラスから継承する必要があると常に感じています。
使用する推奨デザインパターンはありますか? 目的は、要件に応じて (コンパイル時に決定される) 「読み取りアクセスのみ」オブジェクトと「読み取り書き込みアクセス」オブジェクトを別々に返すことができるようにすることです。ソリューション パターンにより、継承、クラス C、D などの層を簡単に追加できるようになるとよいでしょう。
多重継承の問題がここで発生し、他の多くの場所で詳細に議論されていることを私は知っています。しかし、私の質問は「複数の継承を使用せずに名前空間アクセスパターン内で定義されたインターフェイスを実装する方法」ではなく (それを行うための最良の方法を学びたいのですが)、むしろ、どのように ReadOnly/Writable を定義できますか?クラスのバージョンを個別に作成し、非常に、非常に、面倒になることなく継承もサポートしますか?
ここで価値があるのは、1つの(面倒な)ソリューションです[より良い実装については以下を参照してください]:
namespace accesspattern
{
namespace ReadOnly
{
public class A : IA
{
protected double a;
public double get_a() { return a; }
}
public class B : IB
{
protected int b;
public int get_b() { return b; }
}
}
namespace Writable
{
public class A : ReadOnly.A, IA
{
public void set_a(double value) { base.a = value; }
}
public class B : ReadOnly.B, IB
{
private IA aObj;
public double get_a() { return aObj.get_a(); }
public void set_a(double value) { aObj.set_a(value); }
public void set_b(int value) { base.b = value; }
public B() { aObj = new A(); }
}
}
}
}
更新: これ (以下) は Eugene が話していることだと思います。この実装パターンはかなり良いと思います。クラスの「writeProtected」ビューのみを渡すことで、クラスの状態が変化しないことを必要とするアルゴリズムを実装し、「writeEnabled」ビューのみを使用して、関数が状態回避の変化を引き起こす/引き起こす可能性があることを意味します。
namespace access
{
// usual usage is at least readable
public interface IA { double get_a(); }
public interface IB : IA { int get_b(); }
// special usage is writable as well
namespace writable
{
public interface IA : access.IA { void set_a(double value); }
public interface IB : access.IB, IA { void set_b(int value);}
}
// Implement the whole of A in one place
public class A : writable.IA
{
private double a;
public double get_a() { return a; }
public void set_a(double value) { a = value; }
public A() { }
//support write-protection
public static IA writeProtected() { return new A(); }
public static writable.IA writable() { return new A(); }
}
// implement the whole of B in one place and now no issue with using A as a base class
public class B : A, writable.IB
{
private int b;
public double get_b() { return b; }
public void set_b(int value) { b = value; }
public B() : base() { }
// support write protection
public static IB writeProtected() { return new B(); }
public static writable.IB writable() { return new B(); }
}
public static class Test
{
static void doSomething(IA a)
{
// a is read-only
}
static void alterState(writable.IB b)
{
// b is writable
}
static void example()
{
// Write protected
IA a = access.A.writeProtected();
IB b = access.B.writeProtected();
// write enabled
writable.IA A = access.A.writable();
writable.IB B = access.B.writable();
Console.WriteLine(a.get_a());
B.set_b(68);
doSomething(A); // passed as writeprotected
alterState(B); // passed as writable
}
}
}