在复杂的对象结构中创建类的只读版本
在我当前的项目中,我需要能够拥有类的可编辑版本和只读版本。因此,当类显示在列表或 PropertGrid 中时,用户将无法编辑他们不应该被允许的对象。
为此,我遵循下图所示的设计模式。我从一个只读接口 (IWidget
) 开始,然后创建一个实现该接口 (Widget
) 的可编辑类。接下来,我创建一个只读类 (ReadOnlyWidget
),它简单地包装可变类并实现只读接口。
对于许多不同的不相关类型,我都遵循这种模式。但现在我想在我的程序中添加一个搜索功能,它可以生成包含任何类型的结果,包括可变和不可变版本。所以现在我想添加另一组接口(IItem
、IMutableItem
)来定义适用于所有类型的属性。因此,IItem
定义了一组通用的不可变属性,而IMutableItem
定义了相同但可编辑的属性。最后,搜索将返回 IItems
的集合,随后可以根据需要将其转换为更具体的类型。
然而,我不确定是否正确设置了 IMutable
和 IItem
的关系。现在,我有每个接口(IWidget
、IDooHickey
)继承自IItem
,然后是可变类(Widget,
DooHickey
) 此外还实现了 IMutableItem
。
或者,我还认为可以将 IMutableItem
设置为从 IItem
继承,这将使用同时具有 get 和 set 访问器的新属性隐藏其只读属性。然后,可变类将实现 IMutableItem
,而只读类将实现 IItem
。
如果有任何关于此的建议或批评,我将不胜感激。
类图
代码
public interface IItem
{
string ItemName { get; }
}
public interface IMutableItem
{
string ItemName { get; set; }
}
public interface IWidget:IItem
{
void Wiggle();
}
public abstract class Widget : IWidget, IMutableItem
{
public string ItemName
{
get;
set;
}
public void Wiggle()
{
//wiggle a little
}
}
public class ReadOnlyWidget : IWidget
{
private Widget _widget;
public ReadOnlyWidget(Widget widget)
{
this._widget = widget;
}
public void Wiggle()
{
_widget.Wiggle();
}
public string ItemName
{
get {return _widget.ItemName; }
}
}
public interface IDoohickey:IItem
{
void DoSomthing();
}
public abstract class Doohickey : IDoohickey, IMutableItem
{
public void DoSomthing()
{
//work it, work it
}
public string ItemName
{
get;
set;
}
}
public class ReadOnlyDoohickey : IDoohickey
{
private Doohickey _doohicky;
public ReadOnlyDoohickey(Doohickey doohicky)
{
this._doohicky = doohicky;
}
public string ItemName
{
get { return _doohicky.ItemName; }
}
public void DoSomthing()
{
this._doohicky.DoSomthing();
}
}
In my current project I need to be able to have both editable and read-only versions of classes. So that when the classes are displayed in a List or PropertGrid the user is not able to edit objects they should not be allowed to.
To do this I'm following the design pattern shown in the diagram below. I start with a read-only interface (IWidget
), and then create an edtiable class which implements this interface (Widget
). Next I create a read-only class (ReadOnlyWidget
) which simply wraps the mutable class and also implements the read only interface.
I'm following this pattern for a number of different unrelated types. But now I want to add a search function to my program, which can generate results that include any variety of types including both mutable and immutable versions. So now I want to add another set of interfaces (IItem
, IMutableItem
) that define properties which apply to all types. So IItem
defines a set of generic immutable properties, and IMutableItem
defines the same properties but editable. In the end a search will return a collection of IItems
, which can then later be cast to more specific types if needed.
Yet, I'm not sure if I'm setting up the relationships to IMutable
and IItem
correctly. Right now I have each of the interfaces (IWidget
, IDooHickey
) inheriting from IItem
, and then the mutable classes (Widget
, DooHickey
) in addition also implement IMutableItem
.
Alternatively, I was also thinking I could then set IMutableItem
to inherit from IItem
, which would hide its read-only properties with new properties that have both get and set accessors. Then the mutable classes would implement IMutableItem
, and the read-only classes would implement IItem
.
I'd appreciate any suggestions or criticisms regarding any of this.
Class Diagram
Code
public interface IItem
{
string ItemName { get; }
}
public interface IMutableItem
{
string ItemName { get; set; }
}
public interface IWidget:IItem
{
void Wiggle();
}
public abstract class Widget : IWidget, IMutableItem
{
public string ItemName
{
get;
set;
}
public void Wiggle()
{
//wiggle a little
}
}
public class ReadOnlyWidget : IWidget
{
private Widget _widget;
public ReadOnlyWidget(Widget widget)
{
this._widget = widget;
}
public void Wiggle()
{
_widget.Wiggle();
}
public string ItemName
{
get {return _widget.ItemName; }
}
}
public interface IDoohickey:IItem
{
void DoSomthing();
}
public abstract class Doohickey : IDoohickey, IMutableItem
{
public void DoSomthing()
{
//work it, work it
}
public string ItemName
{
get;
set;
}
}
public class ReadOnlyDoohickey : IDoohickey
{
private Doohickey _doohicky;
public ReadOnlyDoohickey(Doohickey doohicky)
{
this._doohicky = doohicky;
}
public string ItemName
{
get { return _doohicky.ItemName; }
}
public void DoSomthing()
{
this._doohicky.DoSomthing();
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
当您需要只读副本时可以创建另一个对象吗?如果是这样,那么您可以使用包含的代码中的技术。如果没有,我认为包装器可能是您最好的选择。
Is it OK to create another object when you need a readonly copy? If so then you can use the technique in the included code. If not, I think a wrapper is probably your best bet when it comes to this.
我建议对于每个主类或接口,定义三个类:“可读”类、“可更改”类和“不可变”类。只有“可变”或“不可变”类才应作为具体类型存在;它们都应该派生自抽象的“可读”类。想要安全地存储对象并知道它永远不会改变的代码应该存储“不可变”类;想要编辑对象的代码应该使用“changeable”类。不会写入某些内容但不关心它是否永远保持相同值的代码可以接受“可读”基本类型的对象。
可读版本应包含公共抽象方法
AsChangeable()
、AsImmutable()
、公共虚拟方法AsNewChangeable()
和受保护虚拟方法AsNewImmutable()
。 “可更改”类应定义AsChangeable()
以返回this
,并定义AsImmutable
以返回AsNewImmutable()
。 “不可变”类应定义AsChangeable()
以返回AsNewChangeable()
和AsImmutable()
以返回this
。所有这一切的最大困难是,如果尝试使用类类型而不是接口,继承就不能很好地工作。例如,如果希望有一个从
BasicCustomer
继承的EnhancedCustomer
类,则ImmutableEnhancedCustomer
应该从ImmutableBasicCustomer
继承code> 和ReadableEnhancedCustomer
,但 .net 不允许这种双重继承。人们可以使用接口 IImmutableEnhancedCustomer 而不是类,但有些人会认为“不可变接口”有点奇怪,因为模块不可能以这样的方式定义接口:外部人员可以使用它,而无需允许外部人员定义自己的实现。I would suggest that for each main class or interface, there be three defined classes: a "readable" class, a "changeable" class, and an "immutable" class. Only the "changeable" or "immutable" classes should exist as concrete types; they should both derive from an abstract "readable" class. Code which wants to store an object secure in the knowledge that it never changes should store the "immutable" class; code that wants to edit an object should use the "changeable" class. Code which isn't going to write to something but doesn't care if it holds the same value forever can accept objects of the "readable" base type.
The readable version should include public abstract methods
AsChangeable()
,AsImmutable()
, public virtual methodAsNewChangeable()
, and protected virtual methodAsNewImmutable()
. The "changeable" classes should defineAsChangeable()
to returnthis
, andAsImmutable
to returnAsNewImmutable()
. The "immutable" classes should defineAsChangeable()
to returnAsNewChangeable()
andAsImmutable()
to returnthis
.The biggest difficulty with all this is that inheritance doesn't work terribly well if one tries to use class types rather than interfaces. For example, if one would like to have an
EnhancedCustomer
class which inherits fromBasicCustomer
, thenImmutableEnhancedCustomer
should inherit from bothImmutableBasicCustomer
andReadableEnhancedCustomer
, but .net doesn't allow such dual inheritance. One could use an interfaceIImmutableEnhancedCustomer
rather than a class, but some people would consider an 'immutable interace' to be a bit of a smell since there's no way a module that defines an interface in such a way that outsiders can use it without also allowing outsiders to define their own implementations.放弃所有进入这里的人的希望!
我怀疑从长远来看你的代码会非常混乱。您的类图表明给定对象中的所有属性都是可编辑的(或不可编辑的)。或者您的(我)可变接口是否引入了与“核心”/继承类分开的不可变或不可变的新属性?
无论哪种方式,我认为您最终都会玩带有属性名称变体和/或隐藏继承属性的游戏
标记接口也许?
考虑使您的类中的所有属性都可变。然后实现IMutable(我不喜欢IItem这个名字)和IImutable作为标记接口。也就是说,接口主体中实际上没有定义任何内容。但它允许客户端代码将对象作为可变引用来处理,例如。
这意味着(a)您的客户端代码运行良好并尊重其可变性,或者(b)所有对象都由强制给定对象可变性的“控制器”类包装。
Abandon hope all ye who enter here!!!
I suspect that in the long run your code is going to be very confusing. Your class diagram suggests that all properties are editable (or not) in a given object. Or are your (I'm)mutable interfaces introducing new properties that are all immutable or not, separate from the "core"/inheriting class?
Either way I think you're going to end up with playing games with property name variations and/or hiding inherited properties
Marker Interfaces Perhaps?
Consider making all properties in your classes mutable. Then implement IMutable (I don't like the name IItem) and IImutable as a marker interfaces. That is, there is literally nothing defined in the interface body. But it allows client code to handle the objects as a IImutable reference, for example.
This implies that either (a) your client code plays nice and respects it's mutability, or (b) all your objects are wrapped by a "controller" class that enforces the given object's mutability.
可能为时已晚:-),但原因“属性上需要关键字“new”,因为它隐藏了属性...”是 Resharper 中的一个错误,编译器没有问题。请参阅下面的示例:
对于没有接口的情况也是如此。唯一的限制是你不能使用自动属性
Could be too late :-), but the cause "The keyword 'new' is required on property because it hides property ..." is a bug in Resharper, no problem with the compiler. See the example below:
Same for the case without interfaces. The only limitation, you can't use auto-properties