In the history of. NET development, 2.0 is a milestone version. From this version,. NET is better than blue (Java. Among the many new features brought by. NET 2.0, I think generics are the most important and none of them are.
Although generics have been around for many years, even Java has already introduced generics for reference (although syntactic sugar), generic programming methods have not been popularized. On the one hand, because a large number of frameworks are still written in the non-generic era in the past, on the other hand, the generic design model has not been developed and it is time to change.
Here is an example to illustrate these two points. If we have written code for network data capturing, we should be familiar with this Code:
Var request = WebRequest. Create ("http://www.bkjia.com/") as HttpWebRequest;
Or, the same is true:
Var request = HttpWebRequest. Create ("http://www.bkjia.com/") as HttpWebRequest;
You can think about it. Why do we need to do it every time?
There are also similar situations, for example, those who do image processing will be familiar:
Var bm = Image. FromFile ("e: \ me.jpg") as Bitmap;
And
Var bm = Bitmap. FromFile ("e: \ me.jpg") as Bitmap;
I thought about it, but I don't want to understand it. Both of the above statements call the factory method of the parent class and return an instance of a subclass. Obviously, even if you do not understand OCP, you should think intuitively that the implementation of the parent class should not be determined by the quilt class. The predecessors who wrote WebRequest and Image may also think that it is inappropriate to directly return the subclass instance. Therefore, the return type of the method signature is changed to the parent class.
Although such behavior deserves serious contempt. However,. NET programmers are mostly well-rounded and well-rounded, so this problem has not been changed for many years.
The ideal design should be like this: each sub-class of the parent class has an independent factory method and returns its own instance. In this way, it was very clumsy before the appearance of generics, but it was worth the candle. However, with generics, it can be precisely implemented.
Taking the Image simulation class as an example, the implementation of Image and BitMap is as follows:
Class Image <T> where T: Image <T>, new ()
{
Public string Path {get; set ;}
Public static T FromFile (string path)
{
Return new T () {Path = path };
}
}
Class Bitmap: Image <Bitmap>
{
}
There is no need for the factory method of Image itself.
You can perform a simple test:
Var path = @ "e: \ me.jpg ";
Var bm = Bitmap. FromFile (path );;
Console. WriteLine (bm. Path );
Console. WriteLine (bm. GetType (). Name );
The output result is as follows:
Path: e: \ me.jpg
Type: Bitmap
To make everyone more familiar, let's take a binary tree in the data structure as an example.
Traditional Tree node classes, regardless of C/C ++/Java, are similar to the following:
Class TreeNode
{
Public TreeNode LeftChild {get; set ;}
Public TreeNode RightChild {get; set ;}
Public TreeNode Parent {get; set ;}
Public int Value {get; set ;}
}
As you know, binary trees are divided into several types: AVL Tree, B tree, and red/black tree. To implement a special binary tree data structure, it is necessary to inherit the TreeNode. Because the type of the Tree node contains members of the basic class, the type is forced to be converted when the child class operates these members. This is similar to the Image and WebRequest examples, it is troublesome to convert the type only when the instance is created.
This is a good opportunity for the generic mode to show its skills. Please refer to the implementation of its parent type:
/// <Typeparam name = "T"> Type of the node. </typeparam>
/// <Typeparam name = "K"> Type of the node value. </typeparam>
Class TreeNode <T, K> where T: TreeNode <T, K> where K: IComparable <K>
{
Public T LeftChild {get; set ;}
Public T RightChild {get; set ;}
Public T Parent {get; set ;}
Public K Value {get; set ;}
}
Then, implement any special binary tree structure. For example, RBTreeNode represents a red-black tree node. You can do this:
Class RBTreeNode: TreeNode <RBTreeNode, Int32>
{
/// <Summary>
/// Specifies whether the color of the Tree node is red.
/// </Summary>
Public bool IsRed {get; set ;}
Public override string ToString ()
{
Return this. Value + "," + (this. IsRed? "R": "B ");
}
}
This is the AVL Tree:
Class AvlTreeNode: TreeNode <AvlTreeNode, Int32>
{
/// <Summary>
/// Node balancing
/// </Summary>
Public int Balance {get; set ;}
Public override string ToString ()
{
Return "Balance:" + Balance + ", Value:" + this. Value;
}
}
Not only does it fully comply with the OCP principle, but it does not need to be forced to convert the node type.
This is certainly not my first initiative. In fact, there are already many such designs in the. NET Framework, such as the IComparable <T> interface. There are also many excellent frameworks that adopt similar designs, such as The ORM framework NewLife. XCode of dashi.
It seems quite simple, but many people are still stuck in the initial stage of object-oriented language and are not used to this design model. I think this writing method is typical and general enough to get a design pattern. It is also a special advantage and unique charm of. NET.
Speaking of the Design Pattern, the 23 Design patterns proposed by GOF have been outdated for many years and many new models have emerged (for example, for concurrent programming, refer to Wiki Design Pattern ). In the old mode, some are included in the. NET language features, and some have changed their implementations. Especially after the appearance of generics, the implementation of many modes can be much simpler and more elegant.
Do not stir-fry the past cold meals over and over again. The design model should keep pace with the times and will always be a new and dynamic topic.
From XiaoMing