Individual originality, if similar, is a coincidence. Reprinted please note: recently idle and doing things in the West...
2017-03-09 17:30
When I do desktop applets, I often see such scenarios:
Select a data, pop up a box or something to change, there are two options [confirm] and [cancel], after the point cancel all changes can not be applied to the original data.
Manual control, that is, writing a lot of code to control each time, is really annoying. There are many layers in the Model, which is not easy to control, but there is no good way to do it.
Later, I wondered if I could write a control of the interface, modify the side of the interface, retain the reference to the original data, and create an identical copy (there is no reference to the original data). All the operations are only for the data copied out, otherwise, the page where the original data is located will change at the same time.
If you finally choose [cancel], you don't care about anything; if you choose [confirm], you use the copied data to "completely replace" the original data. Note that there is no direct equal sign, which will lead to the loss of reference to the original data, and the operation is useless.
So the interface looks like this:
public interface IUIModel<T> where T : class, new()
{
T Clone();
void UpdateBy(T other);
}
Chestnut:
public class SomeModel : IUIModel<SomeModel>
{
public int SomeProperty { get; set; }
public SomeModel Clone()
{
SomeModel copy = new SomeModel();
copy.SomeProperty = this.SomeProperty;
return copy;
}
public void UpdateBy(SomeModel other)
{
Assert.NotNull(other);
this.SomeProperty = other.SomeProperty;
}
}
Chestnuts of some practical value:
public class AnotherModel : IUIModel<AnotherModel>
{
public string AnotherProperty { get; set; }
public SomeModel SomeModel { get; set; }
public AnotherModel Clone()
{
AnotherModel copy = new AnotherModel();
copy.AnotherProperty = this.AnotherProperty;
copy.SomeModel = this.SomeModel.Clone();
return copy;
}
public void UpdateBy(AnotherModel other)
{
Assert.NotNull(other);
this.AnotherProperty = other.AnotherProperty;
this.SomeModel.UpdateBy(other.SomeModel);
}
}
Yes, abolish quotation!
Later on, I suddenly found repetitive parts:
public AnotherModel Clone()
{
AnotherModel copy = new AnotherModel();
copy.UpdateBy(this);
return copy;
}
Then I feel that this part can be done with an abstract class and write less code.
So there's the parent-child conversion, a wonderful thing that other programmers spray around...
First, for database reasons, two attributes need to be added to the parent class:
public long Pkey { get; private set; }
public State State { get; private set; }
State is an enumeration: Normal, Draft, Removed
The methods that can be placed in the parent class are Clone and UpdateBy for the above two attributes. UpdateBy for other fields in the subclass is implemented in the subclass.
So this generic abstract class appears:
public abstract class UIModelBase<T> where T : class, new()
{
public long Pkey { get; private set; }
public State State { get; private set; }
public T Clone()
{
T copy = Activator.CreateInstance<T>();
(copy as UIModelBase<T>).UpdateBy(this);
return copy;
}
public void UpdateBy(UIModelBase<T> other)
{
this.Pkey = other.Pkey;
this.State = other.State;
this.updateBy(other as T);
}
protected abstract void updateBy(T other);
}
Explain roughly,
Activator.CreateInstance<T>()
This sentence creates an instance of T, and where T: new () ensures that it has a common parametric constructor, so that the creation of an instance part is basically okay.
And behind
(copy as UIModelBase<T>).UpdateBy(this);
It is the child class to the parent class, and then call the parent class's UpdateBy method.
The next UpdateBy method is somewhat "strange".
// The parameter type passed in is the parent UIModelBase < T >, but this.updateBy(other as T) sentence turns the parent class into a subclass.
Generally, subclasses can be converted to parent classes, but the parent rotor class can not be implemented, because the extra part of the parent class in the subclass is unknown, even if these extra parts are lost.
But the reason for success here is that the "parent" introduced by the UpdateBy method is actually an instance of a subclass. It is certainly possible to convert the "subclass" to the same type, because the abstract class itself has no instances.
As follows, suppose there are two classes, A and B:
public class A { }
public class B : A
{
public int AB { get; set; }
}
Then?
A a_1 = new A();
B b_1= a_1 as B;
Assert.Null(b_1);
A a_2 = new B();
B b_2 = a_2 as B;
Assert.NotNull(b_2);
After explaining, look at the chestnuts.
public class SomeModel : UIModelBase<SomeModel>
{
public int SomeProperty { get; set; }
protected override void updateBy(SomeModel other)
{
Assert.NotNull(other, "SomeModel.updateBy.other");
this.SomeProperty = other.SomeProperty;
}
}
public class AnotherModel : UIModelBase<AnotherModel>
{
public string AnotherProperty { get; set; }
public SomeModel SomeModel { get; set; }
protected override void updateBy(AnotherModel other)
{
Assert.NotNull(other, "AnotherModel.updateBy.other");
this.AnotherProperty = other.AnotherProperty;
if (this.SomeModel == null)
{
if (other.SomeModel != null)
{
this.SomeModel = other.SomeModel.Clone();
}
}
else
{
if (other.SomeModel != null)
{
this.SomeModel.UpdateBy(other.SomeModel);
}
else
{
this.SomeModel = null;
}
}
}
}
Test:
AnotherModel left = new AnotherModel();
left.AnotherProperty = "left";
left.SomeModel = new SomeModel();
left.SomeModel.SomeProperty = 0;
Assert.True(left.SomeModel.SomeProperty == 0);
AnotherModel right = left;
right.AnotherProperty = "right";
right.SomeModel.SomeProperty = 1;
Assert.True(left.SomeModel.SomeProperty == 1);
AnotherModel rightCopy = left.Clone();
rightCopy.AnotherProperty = "right copy";
rightCopy.SomeModel.SomeProperty = 2;
Assert.True(left.SomeModel.SomeProperty == 1);
When I recommended this base class to my colleagues, I was despised.