Creating read-only classes in a complex object structure

In my current project, I need to have both editable and readable versions of classes. Thus, when classes are displayed in a List or PropertGrid, the user cannot edit objects, they are not allowed.

To do this, I follow the design pattern shown in the diagram below. I start with a read-only interface ( IWidget ), and then create an editable class that implements this interface ( Widget ). Then I create a read-only class ( ReadOnlyWidget ) that simply wraps the mutable class and also implements a read-only interface.

I follow this pattern for several different unrelated types. But now I want to add a search function to my program that can generate results that include any number of types, including both mutable and immutable versions. So now I want to add another set of interfaces ( IItem , IMutableItem ) that define properties that apply to all types. Thus, IItem defines a set of universal immutable properties, while IMutableItem defines the same properties as editable ones. As a result, the search will return the IItems collection, which can then be added to more specific types, if necessary.

However, I'm not sure if I am establishing a relationship with IMutable and IItem . Right now I have each of the interfaces ( IWidget , IDooHickey ) inheriting from IItem , and then mutable classes ( Widget , DooHickey ) also implement IMutableItem .

Alternatively, I also thought that I could set IMutableItem inherit from IItem , which would hide its read-only properties with new properties that have both get and set accessors. Then mutable classes implement IMutableItem , and read-only classes implement IItem .

I would appreciate any suggestions or criticism regarding any of this.

Class diagram

alt text

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(); } } 
+4
source share
4 answers

Is it possible to create another object if you need a read-only copy? If so, you can use this technique in the included code. If not, I think the wrapper is probably best suited when it comes to this.

 internal class Test { private int _id; public virtual int ID { get { return _id; } set { if (ReadOnly) { throw new InvalidOperationException("Cannot set properties on a readonly instance."); } } } private string _name; public virtual string Name { get { return _name; } set { if (ReadOnly) { throw new InvalidOperationException("Cannot set properties on a readonly instance."); } } } public bool ReadOnly { get; private set; } public Test(int id = -1, string name = null) : this(id, name, false) { } private Test(int id, string name, bool readOnly) { ID = id; Name = name; ReadOnly = readOnly; } public Test AsReadOnly() { return new Test(ID, Name, true); } } 
+1
source

I would suggest that for each main class or interface, there are three specific classes: a "readable" class, a "variable" class, and an "immutable" class. Only “mutable” or “immutable” classes should exist as concrete types; they should come from an abstract "readable" class. Code that wants to keep a protected object in knowledge that it never changes must store a "immutable" class; code that wants to edit the object must use the "changeable" class. Code that is not going to write something, but doesn’t matter if it retains the same value forever, can accept objects of a “readable” base type.

The readable version should include the public abstract methods AsChangeable() , AsImmutable() , the public virtual method AsNewChangeable() and the protected virtual method AsNewImmutable() . Variable classes must define AsChangeable() to return this and AsImmutable to return AsNewImmutable() . Immutable classes must define AsChangeable() to return AsNewChangeable() and AsImmutable() to return this .

The biggest difficulty with this is that inheritance does not work very well if you are trying to use class types rather than interfaces. For example, if you want to have an EnhancedCustomer class that inherits from BasicCustomer , then ImmutableEnhancedCustomer should inherit both from ImmutableBasicCustomer and ReadableEnhancedCustomer , but .net does not allow such double inheritance. You can use the IImmutableEnhancedCustomer interface rather than a class, but some people think that “immutable interaction” can be a little odor, because there is no way the module defines the interface so that outsiders can use it without also allowing outsiders to define their own implementations.

+1
source

Refuse the hope that you are coming here !!!

I suspect that ultimately your code will be very confusing. Your class diagram shows that all properties are edited (or not) in the given object. Or are your (I'm) mutable interfaces introducing new properties that are immutable or not, separate from the "core" / inheriting class?

In any case, I think that you will end up playing games with variations of property names and / or hide inherited properties

Marker Interfaces Perhaps?
Imagine all the properties in your classes are mutable. Then we implement IMutable (I do not like the name IItem) and IImutable as marker interfaces. That is, literally nothing is defined in the interface case. But it allows client code to process objects as a reference to IImutable, for example.

This means that either (a) your client code plays well and respects its variability, or (b) all your objects are wrapped with the "controller" class, which provides this variability of the object.

0
source

It may be too late :-), but the reason “The keyword“ new ”is required for the property because it hides the property ...” is a bug in Resharper, without problems with the compiler. See the example below:

 public interface IEntityReadOnly { int Prop { get; } } public interface IEntity : IEntityReadOnly { int Prop { set; } } public class Entity : IEntity { public int Prop { get; set; } } [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { var entity = new Entity(); (entity as IEntity).Prop = 2; Assert.AreEqual(2, (entity as IEntityReadOnly).Prop); } } 

The same is true for the case without interfaces. The only limitation is that you cannot use auto properties

 public class User { public User(string userName) { this.userName = userName; } protected string userName; public string UserName { get { return userName; } } } public class UserUpdatable : User { public UserUpdatable() : base(null) { } public string UserName { set { userName = value; } } } [TestClass] public class UnitTest1 { [TestMethod] public void TestMethod1() { var user = new UserUpdatable {UserName = "George"}; Assert.AreEqual("George", (user as User).UserName); } } 
0
source

All Articles