Discuss the relationship between factory model (Abstract Factory) and dip

Source: Internet
Author: User

Most people refer to the factory mode as the abstract factory mode in the gof design mode.

This is a common and useful mode. What is the relationship between it and dip principles?

Dip principles

Dip: Dependency inversion principle.

Dip refers to the principle of dependency inversion. It refers to that upper-layer modules should depend on interfaces, and specific classes should depend on abstract interfaces (that is, forced to implement abstract interfaces ). Abstract interfaces are closer to their users (upper-layer modules), so they look like a specific class depends on upper-layer modules. This is called Dependency inversion.

If we strictly follow dip, any new statement violates dip. For example:

class Pic{public:     virtual void draw() = 0;};class Png : public Pic{public:     virtual void draw()     {            printf("draw png\n");     }}void main(){     Png* p = new Png();     p->draw();}

 

Looking at the above Code, the main () function is used as the user of the PIC class library. New PNG () itself violates dip. Is this funny? This kind of code is very common. But it actually violates dip. We can see the PNG class in the Customer Code. The PNG class is a specific class, which indicates that the customer depends on the specific class. Isn't that a violation of dip? Strictly speaking, it is indeed a violation. However, it may not be too bad.How likely is the change of a PNG class?If the PNG class is basically not changed, the violation will be violated. It does not matter. Just as we use the classes in STL, those classes will almost never change, and the customer code will depend on them directly. Why. However, if the PNG class is very likely to change, it is not very good to depend on the specific class at this time.

The above code, if changed:

void main(){     Pic* p = new Png();     p->draw();}

In this case, it will be much better, because only new depends on the specific class, And the pointer returned by new is saved in PIC * P. PIC * will be used in all other places, except for New itself, the rest depends on interfaces. But new itself is still illegal dip. We can draw a picture:

In the figure above, as a customer, main () is strongly dependent (or associated) on the PIC interface, and then has a PNG dependency (dotted line ). Indeed, there are many places in main () that need to call PIC * P, so it is associated. In new PIC (), it is a weak dependency. In fact, the above design is feasible in most cases, and there is no problem, it is also very common.

If the possibility of PNG changes is very high, for example, the PNG constructor changes frequently, or the possibility of creating other PIC subclasses is very high, or even the class name of PNG changes. At this time, the figure above is a bit problematic. After all, main is weak dependent on specific classes.

At this time, we can consider the factory model. (Gof is called abstract factory ).

Abstract Factory)

We can consider evolving the above graph:


We disconnected the dependencies between main () and PNG, and instead introduced a factory class picfactory. Main () is associated with picfactory, while picfactory is dependent on PNG. The code is roughly as follows:

class PicFactory{public:      Pic* makePng()      {              return new Png()      }};void main(){       PicFactory f;       Pic* p = f.makePng();} 

This is a simple factory. What are the benefits of doing so? Because the PNG class is quite variable, we put it into the factory class. The customer main () only needs to use the factory class to create the PIC object. Someone will ask,We just depended on the PNG class and now on the picfactory class. What are the differences between them?The key lies in: the PNG class or other PIC sub-classes are easy to change, while the factory class is relatively stable and dependent on a relatively stable class is always better than the class that is easy to change.

For example, we need to add a new pic subclass called GIF. What we need to do is:

class Gif : public Pic{public:     virtual void draw()     {        printf("Gif::draw\n");     }};class PicFactory{public:         Pic* makePng()         {                return new Png();         }         Pic* makeGif()         {                return new Gif();         }}void main(){          PicFactory f;          Pic* gif = f.makeGif()          gif->draw();}

1. added a GIF class.

2. Add a function makegif () in picfactory ()

3. The customer main can create GIF objects through the factory.

The problem with doing so is that every time a new pic subclass is added, the factory needs to add a function, which is not very comfortable.

You can change the factory class:

typedef enum PicType{Png = 0, Gif};class PicFactory{public:    Pic* makePic(PicType t)    {           Pic* p = nullptr;           switch(t)           {                case Png:                     p = new Png();                     break;                case Gif:                     p = new Gif();                     break;           }           return p;    }}

In this way, every time we add a new pic subclass, we don't need to add a new factory function. We just need to modify the makepic function. It is better than the original one.

Another benefit of the factory model is:The factory itself can also be replaced.

Factory mode (replaceable)

To solve this problem, PNG has another implementation. This PNG can be drawn in the edit control, for example, in the QQ edit box, which is called pngex. There are two ways to modify the factory class at this time:

1. Add a new case in makepic and return the pngex object.

2. Create a new factory class.

The specific usage depends on the specific situation. If we need to change the creation of a group of objects. So it may be better for #2.Generally, one of our factories creates a group of related products. To create another group of products, create another factory.For example, a factory creates a group of PIC objects that only support GDI painting, and a factory that supports direct x painting.

To achieve this effect,First, we need to transform the factory class. We need to create an abstract interface for the factory class. In fact, this is the abstract server mode.

See:

OK. Now main () is dependent on the factory abstract class, which is more flexible. When we want to use another group of products, we can change the factory. For example:

void main(){       PicFactory* f = new GDIFactory();  //     PicFactory* f = new DXFactory();       f->makePic();}

You can directly change the factory to create another product group. Careful students will surely find that,Isn't this new a violation of dip?Yes. Is it dizzy? Indeed, many times there will always be some tangle in the design field. OK. As mentioned earlier, if dip is strictly followed, any new line of code violates dip. The key is to see whether the violation is related. If the target object is stable, the violation does not matter. For example, the factory here, except for the possibility of adding a new derivative factory, will hardly change anything else. Therefore, the violation in this area does not matter. We introduced the factory,This is because the PIC class is quite variable and the factory itself is relatively stable.In this case, the factory model can play a role.

When to introduce the factory model depends on the specific situation. A simple principle is:When the product (PIC class) You want to create is relatively variable, you should consider it.

Of course, the factory model also has some disadvantages: First, it needs to create a factory class, which is a major overhead. The advantage of the factory model is that it can solve the coupling between the customer and the specific product classes and implement the dip principle.

In fact, it is even more common that a factory can create a series of products that do not inherit from the same interface. More general situations should be as follows:




Iterative Evolution

The factory model is very effective and can be seen in many places. But we cannot use it as soon as it comes up, that is, abuse. Because the factory model itself will bring about some problems, at least introduced a new factory class, which is an overhead. There is no best design, but a proper design. When we need to consider the dip principle in some scenarios, the factory model is often very effective. Of course, there is also the abstract server mode. They are often mixed together. We usually design the system as a step-by-step iteration process, always evolving from the simplest design step by step. This evolution is often driven by changes in demand. When demand changes frequently, we evolve to a complex structure. The iterative evolution in this example looks like:





Discuss the relationship between factory model (Abstract Factory) and dip

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.