这是一个架构问题。程序员经常遇到这种封装问题,但我还没有看到一个完整而干净的解决方案。
相关问题:
readonly class design when a non-readonly class is already in place
Controlling read/write access to fields
通常,在OOP范例中,对象将其数据存储在字段中。类自己的方法可以完全访问其字段。当您需要返回值时,您只需返回数据的副本,以便外部代码不会破坏数据。
现在假设数据块很复杂,因此它们本身就封装在类对象中,并且这些对象不容易被复制。现在,如果从某个属性返回此类对象,则外部代码与内部代码具有相同的访问权限。例如,如果您返回List<int>
,则每个人都可以为其添加值。这通常是不受欢迎的。
这个问题通常使用只读包装器 - 在返回之前将完全访问的内部对象包装在只读包装器中。这种方法的问题是包装器可能是包装值的不良替代 - 包装器是一个不同的类。 (如果从可修改的类派生只读包装器(或反之亦然),那么任何人都可以将“只读”对象向上/向下转换为可修改对象,从而破坏保护。)
我想要一个模式:
int
值)具有“公共/只读API”和“私有/可修改的API”。以下是示例界面:
interface IPublicApi {
int GetValue();
}
interface IPrivateApi {
void SetValue(int value);
}
interface IPrivateConsumer {
void OnValueChanged(); //Callback
}
我已经设计了这样的计划。我希望你批评我的解决方案或给出你自己的解决方案。
答案 0 :(得分:1)
有几个子问题需要解决。
我的系统由以下类组成:
ReadableInt
是公共API
ReadableInt.PrivateApi
是原始私有API代理对象
ReadableInt.IPrivateConsumer
是公用私有回调接口
public sealed class ReadableInt {
int _value;
IPrivateConsumer _privateConsumer;
public ReadableInt(IPrivateConsumer privateConsumer, Action<PrivateApi> privateConsumerInitializer) {
_privateConsumer = privateConsumer;
var proxy = new PrivateApi(this);
privateConsumerInitializer(proxy);
}
public int GetValue() {
return _value;
}
private void SetValue(int value) {
_value = value;
_privateConsumer.OnValueChanged();
}
public interface IPrivateConsumer {
void OnValueChanged();
}
public class PrivateApi {
ReadableInt _readableInt;
internal PrivateApi(ReadableInt publicApi) {
_readableInt = publicApi;
}
public void SetValue(int value) {
_readableInt.SetValue(value);
}
}
}
WritableInt
是一些私有API使用者,可能位于另一个程序集中。
public sealed class WritableInt : ReadableInt.IPrivateConsumer {
ReadableInt _readableInt;
ReadableInt.PrivateApi _privateApi;
public WritableInt() {
_readableInt = new ReadableInt(this, Initialize);
}
void Initialize(ReadableInt.PrivateApi privateApi) {
_privateApi = privateApi;
}
public ReadableInt ReadOnlyInt { get { return _readableInt; } }
public void SetValue(int value) {
_privateApi.SetValue(value);
}
void ReadableInt.IPrivateConsumer.OnValueChanged() {
Console.WriteLine("Value changed!");
}
}
可以使用这样的类:
var writeableInt = new WritableInt();
var readableInt = writeableInt.ReadOnlyInt;
这就是系统的工作方式:
ReadableInt.PrivateApi
)通过作为内部类来获得对主要对象(ReadableInt
)私有成员的访问权限。没有上行/下行安全漏洞。ReadableInt.PrivateApi
构造函数已标记为internal
,因此只有ReadableInt
才能创建实例。我找不到更优雅的方法来阻止任何人从ReadableInt.PrivateApi
对象创建ReadableInt
。ReadableInt
需要引用私有API使用者来调用它(通知等)。要将公共API与具体的私有API使用者分离,私有API使用者将被抽象为ReadableInt.IPrivateConsumer
接口。 ReadableInt
通过构造函数接收对ReadableInt.IPrivateConsumer
对象的引用。ReadableInt.PrivateApi
)通过传递给WriteableInt
构造函数的回调(Action<PrivateApi>
)提供给创建者(ReadableInt
)。 非常难看。任何人都可以提出另一种方式吗? WritableInt.OnValueChanged()
方法是私有的,但实际上是公共的,因为它是一种接口方法。这可以通过代理或代理来解决。还有其他办法吗?这个系统有效,但有些部分我并不自豪。当所有部件链接在一起时,我特别不喜欢初始化阶段。这可以以某种方式简化吗?
答案 1 :(得分:0)
这个问题非常有趣。我不是OOP的专家(上帝!我希望我愿意!),但这就是我的方式:
public interface IReadOnlyFoo
{
int SomeValue
{
get;
}
}
public class Foo: IReadOnlyFoo
{
public int SomeValue
{
get;
set;
}
}
public class Bar
{
private Foo foo;
public IReadOnlyFoo Foo
{
get
{
return foo;
}
}
}
它不是很安全,因为你可以将IReadOnlyFoo强制转换为Foo。但我的理念如下:当你演员时,你要对自己承担全部责任。所以,如果你用脚射击自己,那就是你的错。
首先要考虑的是有值类型和引用类型。
为了这个答案,我将为纯数据类型(int,float,bool等)和结构分类值类型。
有趣的是,您使用价值类型int
来解释您的问题。值类型将通过赋值进行复制。因此,对于int,您不需要任何类型的包装器或只读参考机制。这是肯定的。只需使用私有/受保护的setter制作只读属性或属性即可。故事结束。
对于参考类型,您提出的解决方案看起来太复杂了。我会做这样的事情:
public class ReadOnlyFoo
{
private readonly Foo foo;
public ReadOnlyFoo(Foo foo)
{
this.foo = foo;
}
public SomeReferenceType SomeValue
{
get
{
return foo.SomeValue;
}
}
}
public class Foo
{
public int SomeValue
{
get;
set;
}
}
public class Bar
{
private Foo foo;
public readonly ReadOnlyFoo Foo;
public Bar()
{
foo = blablabla;
Foo = new ReadOnlyFoo(foo);
}
}