(Interface) Let the stiff wings fly-from the instance to talk about Oop, factory model and restructuring

Source: Internet
Author: User

Fly with wings, lack of flexibilityCodeIt's like a bird with frozen wings. If you cannot fly, you will lose a little bit of flexibility. We need to bring warm sunshine to the code and let the cold wings fly again. By combining examples and Applying OOP, design patterns, and refactoring, you will see how the code is revived step by step.

To better understand the design concept, the instance should be as simple as possible. However, as demand increases,ProgramIt will become more and more complex. At this time, there is a need to modify the design, and the reconstruction and design mode can be used. Finally, when the design becomes more perfect, you will find that, even if the demand increases, you can be relaxed and free from worries about code design.

Suppose we want to design a media player. Currently, this media player only supports audio files MP3 and WAV. If you don't talk about the design, the player may be very simple:
Public class mediaplayer
{
Private void playmp3 ()
{
MessageBox. Show ("play the MP3 file .");
}

Private void playwav ()
{
MessageBox. Show ("play the wav file .");
}

Public void play (string audiotype)
{< br> switch (audiotype. tolower ()
{< br> case ("MP3"):
playmp3 ();
break;
case ("WAV "):
playwav ();
break;
}< BR >}

Naturally, you will find this design very bad. Because it does not provide a minimum extension for future demand changes. If your design results are like this, when you are overwhelmed by changes in demand
You may want this design to go where it should be, that is, the recycle bin on the desktop. By carefully analyzing this code, it is actually the oldest structure-oriented design. If you want to play not only
Only MP3 and WAV, you will constantly increase the corresponding playing method, and then let the switch clause grow longer and longer until you can't see it.

Well, let's first try the object spirit. According to the idea of OOP, we should regard MP3 and WAV as an independent object. So is that true?
Public class MP3
{
Public void play ()
{
MessageBox. Show ("play the MP3 file .");
}
}

Public class WAV
{
Public void play ()
{
MessageBox. Show ("play the wav file .");
}
}

You already know how to create an object. Even better, you have applied the refactoring method without knowing it, and changed the name of the method in the original spam design to the unified play () method.
In the subsequent design, you will find the key to renaming this way! But it seems that you have not hit the point. The current method to change the mediaplayer Code does not change much in essence.

Since MP3 and WAV are both audio files and they all share the same characteristics of audio files, why not create a common parent class for them?
Public class audiomedia
{
Public void play ()
{
MessageBox. Show ("play the audiomedia file .");
}
}

Now we have introduced the idea of inheritance, and OOP is also an image. If you are proud of it, analyze the real world carefully. In real life, we only play certain types of audio files.
So the audiomedia class is not actually used. In the design, this class will never be instantiated. Therefore, you have to perform an operation and change it to an abstract class. Okay,
The current Code is somewhat OOP:
Public abstract class audiomedia
{
Public abstract void play ();
}

Public class MP3: audiomedia
{
Public override void play ()
{
MessageBox. Show ("play the MP3 file .");
}
}

Public class WAV: audiomedia
{
Public override void play ()
{
MessageBox. Show ("play the wav file .");
}
}

Public class mediaplayer
{
Public void play (audiomedia Media)
{
Media. Play ();
}
}

Let's take a look at the current design, that is, it satisfies the hierarchical relationship between classes and ensures the minimum principle of classes, which is more conducive to expansion (here, you will find it necessary to change the play method name ). Even if you
Added the playback of WMA files. You only need to design the WMA class, inherit audiomedia, and rewrite the play method. The play method of the mediaplayer Class Object
No need to change.

Is it time to end it? Then, the customers of the media player will never be satisfied. They are complaining about the media player. Because they don't want to hear the host's
Commentary: they are more eager to see football stars running on the stadium. That is to say, they want your media player to support video files. It's time for you to suffer again, because while changing the hardware design, the original software
The component design structure seems to be faulty. Because there are many differences between video files and audio files, you can't be lazy. Let the video file object recognize the audio file as a father. You need to design another class object for the video file
Let's assume we support videos in the RM and MPEG formats:
Public abstract class videomedia
{
Public abstract void play ();
}

Public class RM: videomedia
{
Public override void play ()
{
MessageBox. Show ("play the RM file .");
}
}

Public class MPEG: videomedia
{
Public override void play ()
{
MessageBox. Show ("play the mpeg file .");
}
}

Unfortunately, you cannot enjoy the original mediaplayer class once and for all. Because the RM file you want to play is not a subclass of audiomedia.

But don't worry, because you haven't used the interface tool yet (although you can also use abstract classes, but in C #, only single inheritance of classes is supported ). Although the video and audio formats are different, don't forget that they are one type of media. In many cases, they have many similar functions, such as playing. According to the interface definition, You can implement the same interface for a series of objects with the same function:
Public interface imedia
{
Void play ();
}

Public abstract class audiomedia: imedia
{
Public abstract void play ();
}

Public abstract class videomedia: imedia
{
Public abstract void play ();
}

Change the mediaplayer design:
Public class mediaplayer
{
Public void play (imedia Media)
{
Media. Play ();
}
}

Now, from the evolution of the mediaplayer class, we can conclude that when calling the attributes and methods of class objects, we should avoid using specific class objects as passing parameters, instead, it is necessary to pass its abstract object, which is better to pass the interface and completely remove the actual call from the specific object, which can improve the flexibility of the Code.

However, the process is not complete. Although everything looks perfect, we ignore this fact and forget the mediaplayer caller. Remember Article Initial
Switch statement? It seems that we have removed this annoyance very beautifully. In fact, I played a trick here to delay the switch statement. Although in mediaplayer
The code looks neat. In fact, the troubles are simply transferred to the mediaplayer caller. For example, in the main program interface:
Public void btnplay_click (Object sender, eventargs E)
{
Switch (cbbmediatype. selectitem. tostring (). tolower ())
{
Imedia media;
Case ("MP3 ″):
Media = new MP3 ();
Break;
Case ("WAV "):
Media = new WAV ();
Break;
// Other types are omitted;
}
Mediaplayer player = new mediaplayer ();
Player. Play (media );
}
You can select the cbbmediatype combo box to determine which file to play, and then click play.

The design model is now available. This is the best way to create different types based on different situations. First, let's see what products our factory needs to produce? Although there are two different types
Media audiomedia and videomedia (more in the future), but they all implement the imedia interface at the same time, so we can regard it as a product, using the factory Method
Mode. The first is the factory interface:
Public interface imediafactory
{
Imedia createmedia ();
}

Create a factory for each media file object and implement the factory interface in a unified manner:
Public class mp3mediafactory: imediafactory
{
Public imedia createmedia ()
{
Return new MP3 ();
}
}
Public class rmmediafactory: imediafactory
{
Public imedia createmedia ()
{
Return new RM ();
}
}
// Other factories;

Some may ask, why not directly set up a factory for audiomedia and videomedia? It's easy, because in audiomedia and
Videomedia has different types of derivation. If you build a factory for them, you still need to use the switch statement in the createmedia () method. And since this
The two classes both implement the imedia interface and can be considered as a type. Why is it so difficult to ask for the abstract factory mode to generate two types of products?

Some people may ask, even if you use this method, do you need to use the switch statement when determining which factory to create? I admit that this is true. However, the factory model is used.
The direct benefit is not to solve the problem of switch statements, but to delay the generation of objects to ensure the flexibility of the Code. Of course, I still have the last killer, but you will try again later.
Now, the switch statement disappears completely.

Is it really necessary to implement the two abstract classes audiomedia and videomedia? Isn't it easier for its subclass to directly implement interfaces? For the requirements mentioned in this article,
I think you are right, but it is not ruled out that there will be differences between audiomedia and videomedia. For example, the audio file only needs to be provided to the sound card interface, and the video file also needs to be provided to the video card
. If you want MP3, WAV, RM, and MPEG to directly implement the imedia interface, instead of using audiomedia and videomedia, you can design to meet other requirements.
It is also unreasonable. Of course, this is not included in the scope of this article.

the main program interface has slightly changed:
Public void btnplay_click (Object sender, eventargs e)
{< br> imediafactory factory = NULL;
switch (cbbmediatype. selectitem. tostring (). tolower ()
{< br> case ("MP3"):
factory = new mp3mediafactory ();
break;
case ("WAV"):
factory = new wavmediafactory ();
break;
// other types are omitted;
}< br> mediaplayer player = new mediaplayer ();
player. play (factory. createmedia ();
}

Here, let's look back at the mediaplayer class. This class implements the play method and calls the play method of the corresponding media file based on the passed parameters. In
When the factory object is used, it seems that this class Object runs well. If it is a class library or component designer, it provides such an interface for the main interface programmer to call. However, after introducing the factory model,
The mediaplayer class is redundant. Therefore, we should remember that refactoring is not just about adding new content to the original code. We also need
Delete the redundant code decisively.
Public void btnplay_click (Object sender, eventargs E)
{
Imediafactory factory = NULL;
Switch (cbbmediatype. selectitem. tostring (). tolower ())
{
Case ("MP3 ″):
Factory = new mp3mediafactory ();
Break;
Case ("WAV "):
Factory = new wavmediafactory ();
Break;
// Other types are omitted;
}
Imedia media = factory. createmedia ();
Media. Play ();
}

If you have not realized the benefits of the imedia interface at the beginning, you should have understood it here. This interface is used in the factory. This interface is still used in the main program. Interface
What are the benefits? That is, your main program can be compiled without a specific service class. Therefore, even if you add new services, you do not need to change the main program.

However, it seems that this idea of not modifying the main program is still not completed. Have you seen it? In btnplay_click (), some concrete classes are still created using new.
Example. If it is not completely separated from the specific class, once the business of the specific class is changed, for example, a new factory class is added, the main program still needs to be changed. Besides, the annoying switch statement still exists, it seems to be wings
The growth of the cancer, prompting us that although the wings have been revived from the cold world, these wings are still diseased and cannot fly normally.

It's time to use the configuration file. We can put the corresponding information of each media file type in the configuration file, and then select to create a specific object according to the configuration file. In addition, reflection is used to create objects. First, create a configuration file:

<Deleetask>
<Add key = "MP3" value = "wingproject. mp3factory"/>
<Add key = "WAV" value = "wingproject. wavfactory"/>
<Add key = "RM" value = "wingproject. rmfactory"/>
<Add key = "MPEG" value = "wingproject. mpegfactory"/>
</Appsettings>

Then, in the form_load event on the main program interface, read all the key values of the configuration file and fill in the cbbmediatype combo box control:
Public void form_load (Object sender, eventargs E)
{
Cbbmediatype. Items. Clear ();
Foreach (string key in configurationsettings. receivettings. allkeys)
{
Cbbmediatype. item. Add (key );
}
Cbbmediatype. selectedindex = 0;
}

Finally, change the play button of the main program and click the event:
Public void btnplay_click (Object sender, eventargs E)
{
String mediatype = cbbmediatype. selectitem. tostring (). tolower ();
String factorydllname = configurationsettings. receivettings [mediatype]. tostring ();

// Medialibray is the referenced media file and factory assembly;
Imediafactory factory = (imediafactory) activator. createinstance ("medialibrary", factorydllname). Unwrap ();
Imedia media = factory. createmedia ();
Media. Play ();
}

Now the wings of the birds are not only revived, but also capable of flying. At the same time, we also give these wings a stronger function that can fly higher and farther!

Enjoy the freedom to fly. Imagine if we want to add a media file playing function, such as an AVI file. Then, we only need to create the AVI class in the original business program and implement
The current imedia interface inherits the videomedia class. Create the avimediafactory class in the factory business and implement the imediafactory interface.
If the new factory type is wingproject. avifacloud, add the following line to the configuration file:
<Add key = "Avi" value = "wingproject. avifacloud"/>.

What about the main program? There is no need to make any changes, or even re-compile them. These wings can fly freely!

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.