Strategy VS Mixin

Source: Internet
Author: User
Tags scream
If we are not more profound, we will be more complicated.
-- Stephen Hawking

Summary


Design challenges

We will use a very classic example: Duck simulator [1]. As shown in, our program needs to represent Mallard Duck, Redhead Duck, and Rubber Duck ). Since both Mallard Duck and Redhead Duck have the same function "Fly ()", we upgraded this function to the parent Duck class.

Because Rubber Duck is a subclass of Duck, it automatically obtains the Fly () function, but the Rubber Duck cannot Fly! This is a very common design issue. We often organize related classes to promote their common behaviors to the parent class. The advantage of doing so is not only to write a few lines of code, but more importantly, all child classes have the same behavior pattern. For new developers, as long as they understand the parent class, I have learned more than half of the entire class level. This design is more convenient for scenarios where subclass needs to be added continuously during the life cycle of a program, because we don't need to think about all the logic every time we add a subclass.
However, the irony is that if there are many sub-classes, there will inevitably be an alternative like Rubber Duck; it will be even worse if the sub-classes need to be constantly added, every time we raise the subclass behavior to the parent class, we will fear whether there will be an alternative in the future and cannot apply the logic already fixed in the parent class ......
The following describes four different methods to solve this problem.

1st designs: use virtual functions

The simplest method is to set the Fly () function in the parent class as a virtual function and provide a default implementation. Subclass can use the default Implementation of the parent class or rewrite a new implementation as needed, as shown in.

Well, this is not a comfortable design. The Rubber Duck looks like a transfer student. It is lonely and the teacher does not like it. Sometimes, you may even hear Duck, Mallard Duck, and Redhead Duck talk about it quietly: after school, let's play an electric game. Hey, don't let Rubber Duck hear it ......
If you need to constantly add sub-classes, the situation is even worse. As there are more and more alternatives, we can only try our best to ensure that the code reuse in the first small group is consistent with the logic. New users can only let themselves flow.
Another question is: should we provide a Fly () function for Rubber Duck that won't Fly? Some people agree to this because a consistent interface can be used to operate all the Duck. For example, you can write: IList <Duck> ducks = new List <Duck> ();
Ducks. Add (new MallardDuck ());
Ducks. Add (new RedheadDuck ());
Ducks. Add (new Rubber Duck ());
Foreach (Duck duck in ducks)
{
Duck. Fly ();
}

Some people hate this. The reason is that OO is most attractive because it is the closest Programming Method to human thinking. A good design can be very concise and natural, so no additional documentation is required. The Fly () function provided by Rubber Duck is undoubtedly an exception. The more such exceptions, the more confusing people will be. It's like a person's resume clearly says "proficient in. net", but does not understand C # Or VB. Isn't it strange to do this just to be consistent with others' resumes?
So what if I use a narrow interface?Source code download:VirtualMethod.rar(VS2005 console Project)

2nd designs: narrow Interfaces

As shown in, a concept is extracted in this design: Flyable.

Please note how important it is to explicitly present a concept: now we all know which functions should be implemented to be "Flying" and know that Mallard Duck and Redhead Duck will fly, rubber Duck does not fly.
But this design also has a major drawback: Mallard Duck and Redhead Duck Fly () are repetitive code, but we cannot promote it to the Flyable interface, because. only abstract functions can be defined in the. net interface. Compared with the 1st types of designs, the parent class does not have enough control over the subclass, and adding a subclass is also quite troublesome.Download source code: Interface.rar (VS2005 console Project)

3rd designs: Application Strategy Model

This time, the concept we extracted is not "whether to fly", but "how to fly )".

The child classes of Duck no longer need to implement the Fly () function repeatedly. Now they only need to specify a suitable flight mode. In particular, we are no longer afraid to add new Duck subclasses-if the new Duck subclass requires a new flight method, we only need to add another implementation class of the FlyBehavior interface. This is a perfect design! Of course, it seems a little complicated, if you have to pick out something wrong.Download source code: Strategy.rar(VS2005 console Project)

4th designs: Use Mixin

Mixin was once a dedicated secret technology for Ruby and other dynamic languages. I didn't expect that the. net static language could even learn eight points, and I also got a name named Extension Methods. However, it is not just as simple as "adding some functions to classes that cannot modify source code by using the syntactic sugar". It can give us more choices. Do you still remember the disadvantages of the 2nd designs (using narrow interfaces? Yes, the Fly () of Mallard Duck and Redhead Duck are repeated codes. Extension Methods can be used to solve this problem.

Note: I used a more visual representation of the relationship between FlyModule and Flyable, which is not officially recognized in UML notation.Source code download: Mixin1.rar(VS2008 console Project)

Strategy VS Mixin

Now let's consider adding a Quack () function for all Duck. Mallard Duck and Redhead Duck can scream (quack), while Rubber Duck can only scream (squeak ). How will Strategy and Mixin respond to this demand change?

1. Strategy

We can follow FlyBehavior to add another QuackBehavior. Although it seems that you need to do a lot of work (adding one interface and two classes), you can note that there are very few codes to be modified, most of the work is on the addition of code (in line with the open-close principle); and the addition of code as long as according to the original code, it is basically not necessary to move your mind-these are the characteristics of excellent design.Source code download: Strategy2.rar(VS2005 console Project)

2. Mixin

As shown in, the Quackable and Squeakable interfaces are added, which are quite simple and direct.Source code download: Mixin2.rar(VS2008 console Project)

Which one do you like, Strategy and Mixin?

Strategy will make the concept more centralized-you only need to check which functions are available in the Duck abstract class, and you will know about the entire class level. In addition, you can also see the implementation methods of FlyBehavior. The unified wide interface makes the Client code simple and consistent. Specifically, Strategy allows you to dynamically change the implementation of FlyBehavior and QuackBehavior during the runtime, which cannot be implemented by Mixin in. net.

If you are a supporter of narrow interfaces, you may choose Mixin. But looking at the figure above, there will be a sense of disorder and disorder. Mixin is more suitable for implementing the concept of independence or XXUtility. Mixin is simple, direct, and lightweight.

Mixin and Template Method

Mixin is very suitable for implementing "no matter who you are, as long as you provide the CompareTo () function, you can immediately get LessThan (), GreaterThan (), similar to (), and LessEqual () for free () and GreaterEqual.

Client code: IList <Duck> ducks = new List <Duck> ();

MallardDuck duck1 = new MallardDuck (1 );
MallardDuck duck2 = new MallardDuck (2 );
MallardDuck duck3 = new MallardDuck (2 );

Console. WriteLine ("duck1 <duck2? {0} ", duck1.LessThan (duck2 ));
Console. WriteLine ("duck1> duck2? {0} ", duck1.GreaterThan (duck2 ));
Console. WriteLine ("duck1 <duck3? {0} ", duck1.LessThan (duck3 ));
Console. WriteLine ("duck2 <= duck3? {0} ", duck2.LessEqual (duck3 ));

Source code download: Mixin3.rar(VS2008 console Project)

Previously, Abstract Class was used only to implement the same semantics.

Here, Mixin has many advantages. First, we have extracted a clear, independent, and general concept of IComparable. Second, IComarable + CompareModule is more reusable. In this example, because the content in IComarable + CompareModule is not related to the domain, we should extract the content from it so that Mallard Duck can focus on domain-related issues, the program structure is clearer and more readable.

However, the above example cannot be regarded as a real Template Method. The real Template Method mode is to extract the algorithms that all sub-classes share to the parent class for completing a task. sub-classes are only responsible for implementing one part of the algorithm, you do not need to worry about all the steps of the entire algorithm and the sequence and conditions of these steps. For example, the elephant refrigerator is divided into three steps. In fact, the pig refrigerator is also divided into three steps, and any East suit refrigerator is divided into three steps, in this way, we found an algorithm that can be extracted to "Install the refrigerator ".

We extract concepts or algorithms not only for "code" reuse, but for a few less lines of code, which only saves a little effort. More importantly, we want to save our brains-we can understand existing code more quickly and deeply. If we need to add new code, we don't need to think about all the logic again.

The method for tea and coffee is very similar. You must first boil the water, then put the tea or instant coffee into the cup, and then fill it with water. In addition, you need to consider a lot of details, such as using 80 degrees of water for tea and 95 degrees of best for coffee; if you are using tea, you can add two chrysanthemums and add more sugar for coffee; for tea, you can directly add water to the renewal cup, but for coffee, you cannot renew the cup ...... If one day I want to drink black sesame paste, do I have to think about all the details again? There is no way in real life, but in the programming world, you can use the Template Method mode to escape this annoying job.

Is it easier to live in the programming world than in the real world?

However, this article does not want to describe the many advantages of the Template Method, but wants to ask the question: Is it better to use the Mixin Method to implement the Template Method mode instead of Abstract Class? One obvious benefit is that it is not limited by. net Single inheritance.

. Net only allows single inheritance. A Class can only inherit one Abstract Class. Sometimes this is really uncomfortable. For example, to implement a "black sesame paste and ice planer", you can only use a combination method in the past.

If you use Mixin, you can use more inheritance.

Wait, isn't multi-inheritance the source of complexity and chaos? Yes, using multi-inheritance in C ++ does have many traps and taboos [3]. The main cause of the complexity of Multi-inheritance is the ambiguity between attributes and functions (ambiguity). If the child class inherits multiple parent classes that have functions or attributes of the same name, when the subclass reloads these functions or the Client code wants to call these functions, the compiler does not know which parent class function should be reloaded or called. After years of practice and discussion, people realized that they had to impose restrictions on multi-inheritance to reduce the possibility of confusion .. Net Interface + explicit Interface implementation is such a simple and elegant compromise. Now,. net has slightly relaxed the restrictions and allows the use of Interface + Extension Methods to inherit multiple non-virtual functions, making the multi-inheritance function in. net more powerful.

Template VS Freedom

Are you afraid to use inheritance? Have you ever been afraid of the Template Method mode? I once thought it was a crazy idea to upgrade the Core Algorithm of sub-classes to the parent class. Inheritance will cause the subclass to strongly depend on the parent class, especially when the Template Method mode is used, the subclass will be strongly bound by the parent class. I don't like restraint. I like freedom. In particular, I also remember the design principle of "combination is better than inheritance. So I separated XxxUtility and Xxxxxxer, and my Abstract Class became an empty shell. Later, I suddenly found that every time I add a subclass, I had to think about all the implementation steps and details again. It was equally scary, especially when more than 50 subclasses were waiting for you to add them. Inheritance makes subclass highly dependent on the parent class. This can be seen as a disadvantage, but it can also be said that, the parent class has a strong "control" on the subclass (especially when using the Template Method mode), which makes the subclass simple and uniform, just like the xibing in the same model. Fortunately, inheritance and combination are not an alternative. We can compromise between discipline and freedom. Promote the core algorithm of the subclass to the parent class to form the Template Method mode, and extract other repeated code to form a Strategy or Mixin before using it. Don't be afraid. No one can make perfect improvements at the beginning of the design, unless he has previously done similar programs or a thorough crowdsourced security testing.

References

[1] Freeman et al,Head First Design Patterns. O 'Reilly, 2004.
License plate:In-depth introduction to the design model (English version). Southeast University Press, 2005.

[2] translated by Thomas et al and Sun Yong,Chinese version of Programming Ruby. Electronic Industry Press, 2007.

[3] Meyers, translated by Hou Jie,Valid C ++ Chinese Version. Huazhong University of Science and Technology Press, 2001. -- cla43: wise use of multi-inheritance.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.