Talk about Oop, factory model and reconstruction from instances

Source: Internet
Author: User

To better understand the design concept, the instance should be as simple as possible. However, as demand increases, programs 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)
{
Switch (audiotype. tolower ())
{
Case ("MP3 "):
Playmp3 ();
Break;
Case ("WAV "):
Playwav ();
Break;
}
}
}

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 overwhelming demand changes, you may want to make the design where it should go, is the desktop recycle bin. By carefully analyzing this code, it is actually the oldest structure-oriented design. If you want to play not only MP3 and wav files, you will constantly increase the corresponding playing method, and then let the switch clause grow longer and longer until it is invisible to you.

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 fact, in real life, we only play some specific types of audio files, so this 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. Now the 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 have 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 mediaplayer objects does not need to be changed at all.

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 watch football matches, they only want to hear the comments from the host. 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 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. Suppose 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. Do you still remember the switch statement at the beginning of the article? It seems that we have removed this annoyance very beautifully. In fact, I played a trick here to delay the switch statement. Although the Code in the mediaplayer looks neat, 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 of media audiomedia and videomedia (more in the future), they all implement the imedia interface, so we can regard it as a product, use the factory method. 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 there are different types of derivation in audiomedia and videomedia. If you build a factory for them, you still need to use the switch statement in the createmedia () method. Since both classes implement the imedia interface, we can think of it as a type. Why bother asking 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 direct advantage of using the factory mode is not to solve the difficulty of switch statements, but to delay the generation of objects to ensure the flexibility of the Code. Of course, I still did not make the last move. Later, you will find that the switch statement will actually disappear 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? I think you are right about the requirements mentioned in this article, 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 interface. If you want MP3, WAV, RM, and MPEG to directly implement the imedia interface without using audiomedia or videomedia, it is unreasonable to design the interface to meet other requirements. Of course, this is not included in the scope of this article.

Now the main program interface has changed slightly:

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;
}
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. When there is no factory object, it seems that this class Object runs very well. If it is a class library or component designer, it provides such an interface for the main interface programmer to call. However, after the factory mode is introduced, it is unnecessary to use the mediaplayer class in it. Therefore, we should remember that refactoring is not just about adding new content to the original code. When we find unnecessary designs, we also need to 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. What are the benefits of using interfaces? 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 instances of specific classes are still created using new. 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 a cancer that breeds on wings, prompting us that although the wings have been revived from the frozen 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 ();
Imediafactory factory = (imediafactory) activator. createinstance ("medialibrary", factorydllname). Unwrap (); // medialibray is the referenced media file and factory assembly;
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 an AVI class in the original business program, implement the imedia interface, and inherit 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.

From: http://www.uml.org.cn/mxdx/200605161.htm

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.