I didn't know anything at that time! I should judge her based on her behavior, not her words. She made my life colorful and I really shouldn't have left her to run out. I should have guessed the Warmth behind her bid-loving trick. How self-contradictory the flowers are! I was too young to love her.
-- San ekzhouperry
From the little prince
Summary
If the Extension Method can dynamically select the object to be extended ......
******
Here is a very interesting design. The Quackable and Squeakable interfaces have Abstract Functions WhoAmI () and Quack () with the same function signature (). When both the Quackable and Squeakable interfaces are implemented by Marllard Duck (and Redhead Duck), the Quack () Methods of the two versions are mixed. In this way, you can dynamically decide whether to use Quackable. Quack () or Squeakable. Quack () at runtime.
How do you feel? Is it messy and powerful?
It feels very powerful because Mallard Duck is like a strange guy. Changing a mask (interface) is completely different and dynamic. Previously, this "Dynamic Change of object behavior during runtime" function was only available in Strategy. Although the operating principles behind it are different, this design can still give a feeling that the behavior of the duck. Quack () method is "dynamic.
Can it be used as a substitute for Strategy? No, never. The semantics shown by Strategy is that "Mallard Duck has a way to tweet-screamOr"(The example of Strategy can be referred to in the previous article ). Let's take a look at this. Mallard Duck implements the Quackable and Squeakable interfaces at the same time. The semantics shown is that "Mallard Duck can both coolAt the same timeWe can scream again, "which is contrary to our intention.
The Strategy Mode is to extract the object behavior into an explicit concept. One of the major advantages is that you only need to see which classes implement the QuackBehavior interface and you will know the total number of methods for calling. This is completely invisible in Figure 1's design.
The difficulty is that Strategy is Strategy, which is unique in the sky and in the ground?
Why did we accidentally make a spam design?
Before you rush to throw it into the recycle bin, let's make another assumption: What if you can dynamically decide which interface Mallard Duck can implement during the runtime?
Source code download: GeekDesign.rar(VS2008 console Project)
Dynamic implementation Interface
This design is only a little different from the design in Figure 1: Mallard Duck (and Redhead Duck) does not implement the Quackable interface or Squeakable interface during compilation. In this way, at the beginning (during the compilation phase), Mallard Duck had no ability to tweet, but had the potential to tweet (because it implemented the WhoAmI () function ). During runtime, The Quackable interface is dynamically mixed into the QuackModule by enabling Mallard Duck to dynamically implement the Quackable interface. the Quack () function has the ability to scream; or dynamically implement the Quackable interface, so that it is dynamically mixed into Squeakable. quack () has the ability to scream.
Source code download:DuckTyping.rar (VS2008 console Project)
How is it? Although it only changes a little bit, it feels completely different. Of course, there are some differences between "dynamically mixing different algorithms" in this example and "dynamically replacing different algorithms" in Strategy. However, in any case, our design toolbox has more flexible tools.
But how is it implemented? The NDuck is an open source class library on CodeProject. I know many people have a headache when they hear about open-source projects or third-party projects. However, please believe me that NDuck is really super small and super simple-only a few hundred lines of small to all source code, simple to use without any configuration as long as a NDuck. dll file is referenced.
Appendix
1. Duck Typing Introduction
Duck Typing is a dynamic-type programming method. In this way, the valid semantics is determined based on the method and attribute set of an object, rather than the class inherited by this object. (I like it. Wang Hou will be Xiang, ning has planted hu ?) Duck Typing is named Duck Test.
Duck Test
If a bird looks like a duck, swims like a duck and quacks like a duck, then it's probably a duck ..
If a bird looks like a duck and starts swimming like a duck and is called a duck, it may be a duck.
In Duck Typing, We only care about whether the object has implemented those aspects that need to be used, rather than the object type. I like Zhang manyu, but I don't want to marry a daughter other than Zhang manyu or Zhang manyu.
In a static language such as C #, we may define the IsEmpty () function to judge whether the string is null ().
Bool IsEmpty (String arg)
{
Return arg. Length <= 0;
}
Although the array or Stream object has the Length attribute, this function cannot be used. To determine whether they are empty, we must write two more IsEmpty () functions.
Bool IsEmpty (Array arg)
{
Return arg. Length <= 0;
}
Bool IsEmpty (Stream arg)
{
Return arg. Length <= 0;
}
In Ruby, a dynamic language, you only need to define an IsEmpty () function. # Determine if arg is null
Def is_empty? (Arg)
Return arg. length <= 0
End
S1 = 'abc' # string
S2 = ''# Empty string
A = [1, 3, 5] # Array
Puts is_empty? (S1) # => false
Puts is_empty? (S2) # => true
Puts is_empty? (A) # => false
2. NDuck Project Overview
Whether you like Duck Typing or not, it is very cumbersome to write three IsEmpty () functions that are almost identical in the C # example above. Is there any way to write only one function?
C # provides the type check during the compilation period, but also provides the method of bypassing the type check-reflection. We can let the IsEmpty () function receive parameters of the Object type, and then use reflection to obtain the parameter Length attribute. Static bool IsEmpty (Object arg)
{
// Obtain the value of the arg Length attribute through reflection
Int length = (int) arg. GetType (). GetProperty ("Length"). GetValue (arg, null );
Return length <= 0;
}
This IsEmpty () function applies to any object with the Length attribute. But never use it for your program. It slows down the program as a snail bait. Besides, it is difficult and intuitive to use reflection. I 'd rather go to the Microsoft headquarters to demonstrate and ask for C # to be changed to Dynamic Language.
Another method is to define a HasLength interface to allow IsEmpty () to program the interface. Public interface HasLength
{
Int Length {get ;}
}
Static bool IsEmpty (HasLength arg)
{
Return arg. Length <= 0;
}
This version of the IsEmpty () function applies to any object that implements the HasLength interface. But we cannot modify the source code of String, Array, and Stream. How can we implement the HasLength interface? Remember the classic "Any problem in computer science can be solved with another layer of indirection? NDuck implements the dynamic interface by dynamically creating a proxy class Duck0, so that the proxy class implements the HasLength interface and acts as the String proxy. For example, string s = "abc ";
HasLength s1 = DuckTyping. Implement <HasLength> (s );
A proxy class Duck0public class Duck0: HasLength will be dynamically created
{
Private string _ obj;
Public Duck0 (string obj)
{
This. _ obj = obj;
}
Int HasLength. Length
{
Get
{
Return this. _ obj. Length;
}
}
}
Then you can use IsEmpty () in this way. Static void Main (string [] args)
{
String s = "abc ";
Int [] a = new int [] {};
HasLength s1 = DuckTyping. Implement <HasLength> (s );
HasLength a1 = DuckTyping. Implement <HasLength> ();
Console. WriteLine (IsEmpty (s1); // output: False
Console. WriteLine (IsEmpty (a1); // output: True
Console. WriteLine (s1.GetType (). Name); // output: Duck0
}
Yes. It takes some time to know which functions of the HasLength interface need to be reflected, and to dynamically generate and compile code. However, NDuck ensures that these tasks are only performed once, and then these dynamically generated proxy classes will be cached for future use. So if your program is used for pacemaker or rocket launch control, I don't recommend you use it, and you don't have to worry too much in other cases.
References
Translated by Thomas et al and Sun Yong,Chinese version of Programming Ruby. Electronic Industry Press, 2007.
Russ Olsen,Design Patterns in Ruby. Addison-Wesley, 2007.
Guenter Prossliner,DuckTyping: Runtime Dynamic Interface Implementation. Codeproject, 2006.
Duck typing. Wikipedia. (A proxy may be required)