Movatterモバイル変換


[0]ホーム

URL:


Jump to content
WikibooksThe Free Textbook Project
Search

Factory method

25% developed
From Wikibooks, open books for an open world
<Computer Science Design Patterns
(Redirected fromComputer Science Design Patterns/Factory method examples)

FacadeComputer Science Design Patterns
Factory method
Flyweight
Table of content

The factory pattern is a design pattern used to promote encapsulation of data representation.

Problem
We want to decide at run time what object is to be created based on some configuration or application parameter. When we write the code we do not know what class should be instantiated.
Solution
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. Its primary purpose is to provide a way for users to retrieve an instance with a known compile-time type, but whose runtime type may actually be different. In other words, a factory method that is supposed to return an instance of the classFoo may return an instance of the classFoo, or it may return an instance of the classBar, so long asBar inherits fromFoo. The reason for this is that it strengthens the boundary between implementor and client, hiding the true representation of the data (see Abstraction Barrier) from the user, thereby allowing the implementor to change this representation at anytime without affecting the client, as long as the client facing interface doesn't change.

Basic Implementation of the Factory Pattern

The general template for implementing the factory pattern is to provide a primary user facing class with static methods which the user can use to get instances with that type. Constructors are then made private/protected from the user, forcing them to use the static factory methods to get objects. The followingJava code shows a very simple implementation of the factory pattern for typeFoo.

publicclassFoo{// Static factory methodpublicstaticFoogetInstance(){// Inside this class, we have access to private methodsreturnnewFoo();}// Guarded constructor, only accessible from within this class, since it's marked privateprivateFoo(){// Typical initialization code goes here}}// End class Foo

With this code, it would be impossible for a client of the code to use thenew operator to get an instance of the class, as is traditionally done:

// Client codeFoof=newFoo();// Won't Work!

because the constructor is markedprivate. Instead, the client would have to use the factory method:

// Client codeFoof=Foo.getInstance();// Works!

It should be noted that even within a programming language community, there is no general consensus as to the naming convention of a factory method. Some suggest naming the method with the name of the class, similar to a normal constructor, but starting with a lowercase. Others say that this is confusing, and suggest using an accessor type syntax, like thegetInstance style used above, though others complain that this may incorrectly imply a singleton implementation. Likewise, some offernewInstance, but this is criticized as being misleading in certain situations where a strictlynew instance may not actually be returned (once again, refer to the singleton pattern). As such, we will not attempt to follow any particularly rigid standard here, we will simply try to use a name that makes the most sense for our current purposes.

Factory Pattern Implementation of the Alphabet

That's great, you know how to implement a real simple factory pattern, but what good did it do you? Users are asking for something that fits into typeFoo, and they're getting an instance of the classFoo, how is that different from just calling the constructor? Well it's not, except that you're putting another function call on the stack (which is a bad thing). But that's only for the above case. We'll now discuss a more useful use of the factory pattern.Consider a simple type calledLetter, representing a letter in the alphabet, which has the following client facing interface (i.e., public instance methods):

chartoCharacter();booleanisVowel();booleanisConsonant();

We could implement this easily enough without using the factory method, which might start out something like this:

publicclassLetter{privatecharfTheLetter;publicLetter(charaTheLetter){fTheLetter=aTheLetter;}publicchartoCharacter(){returnfTheLetter;}publicbooleanisVowel(){//TODO: we haven't implemented this yetreturntrue;}publicbooleanisConsonant(){// TODO: we haven't implemented this yetreturnfalse;}}// End class Letter

Fairly simple, but notice we haven't implemented the last two methods yet. We can still do it pretty easily. The first might look like this:

publicbooleanisVowel(){returnfTheLetter=='a'||fTheLetter=='e'||fTheLetter=='i'||fTheLetter=='o'||fTheLetter=='u'||fTheLetter=='A'||fTheLetter=='E'||fTheLetter=='I'||fTheLetter=='O'||fTheLetter=='U';}

Now that's nottoo bad, but we still need to doisConsonant. Fortunately, we at least know in this case that if it's a vowel, it's not a consonant, and vice versa, so our last method could simply be:

publicbooleanisConsonant(){return!this.isVowel();}

So what's the problem here? Basically, every time you call either of these methods, your program has to do all that checking. Granted, this isn't a real heavy burden for the Java Runtime Environment, but you can imagine a much more complex, much more time consuming operation. Wouldn't it be great if we could avoid doing this every time we call the method? Let's say, for instance, we could do it once when we create the object, and then not have to do it again. Well sure, we can do that. Here's an implementation that'll do that for us, and we still don't have to use the factory method:

publicclassLetter{privatecharfTheLetter;privatebooleanfIsVowel;publicLetter(charaTheLetter){fTheLetter=aTheLetter;fIsVowel=fTheLetter=='a'||fTheLetter=='e'||fTheLetter=='i'||fTheLetter=='o'||fTheLetter=='u'||fTheLetter=='A'||fTheLetter=='E'||fTheLetter=='I'||fTheLetter=='O'||fTheLetter=='U';}publicchartoCharacter(){returnfTheLetter;}publicbooleanisVowel(){returnfIsVowel;}publicbooleanisConsonant(){return!fIsVowel;}}// End class Letter

Notice how we moved the lengthy operation into the constructor, and stored the result. OK, so now we're all fine and dandy, no? Sure, but let's say you came up with a new idea, a different implementation: you want to split this type into two classes, one class to handle the vowels, and one to handle the consonants. Great, they can both be subclasses of theLetter class, and the user will never know the difference, right? Wrong. How is the client supposed to get at these new classes? They've got code that works perfectly well for them by callingnew Letter('a') andnew Letter('Z'). Now you're going to make them go through all their code and change these tonew Vowel('a') andnew Consonant('Z')? They probably won't be too happy with that. If only you could get new instances of both classes from one method! Well you can, just use a static method in theLetter class, it'll do the same one-time checking as the constructor did, and will return an appropriate instance of the right class. And what do you know, it's a factory method! But that still doesn't do your client much good, they still need to go through and change all thenew Letter()s intoLetter.getLetter().Well, sad to say, it's too late to help them at all, unless you just give up your new implementation. But that illustrates the reason for using the factory method right off the bat. One of the key components of good object oriented programming is that you never know exactly where your code will go in the future. By making good use of the abstraction barrier and using encapsulation-friendly programming patterns, such as the factory pattern, you can better prepare yourself—and your client—for future changes to the specific implementation. In particular, it allows you to use a "big hammer" kind of approach to get something done in a perhaps-less-than-ideal but rapid manner in order to meet deadlines or move ahead with testing,. You can then go back later and refine the implementation—the data representation and algorithms—to be faster, smaller, or what-have-you, and as long as you maintained the abstraction barrier between implementor and client and properly encapsulated your implementation, then you can change it without requiring the client to change any of their code.Well now that I'm sure you're a raving advocate for the factory method, let's take a look at how we would implement it for ourLetter type:

publicabstractclassLetter{// Factory MethodpublicstaticLettergetLetter(charaTheLetter){// Like before, we do a one time check to see what kind of// letter we are dealing with. Only this time, instead of setting// a property to track it, we actually have a different class for each// of the two letter types.if(aTheLetter=='a'||aTheLetter=='e'||aTheLetter=='i'||aTheLetter=='o'||aTheLetter=='u'||aTheLetter=='A'||aTheLetter=='E'||aTheLetter=='I'||aTheLetter=='O'||aTheLetter=='U'){returnnewVowel(aTheLetter);}else{returnnewConsonant(aTheLetter);}}// User facing interface// We make these methods abstract, thereby requiring all subclasses// (actually, just all concrete subclasses, that is, non-abstract)// to implement the methods.publicabstractbooleanisVowel();publicabstractbooleanisConsonant();publicabstractchargetChar();// Now we define the two concrete classes for this type,// the ones that actually implement the type.privatestaticclassVowelextendsLetter{privatechariTheLetter;// ConstructorVowel(charaTheLetter){this.iTheLetter=aTheLetter;}// Nice easy implementation of this method!publicbooleanisVowel(){returntrue;}// This one, too!publicbooleanisConsonant(){returnfalse;}publicchargetLetter(){returniTheLetter;}}// End local class VowelprivatestaticclassConsonantextendsLetter{privatechariTheLetter;// ConstructorConsonant(charaTheLetter){this.iTheLetter=aTheLetter;}publicbooleanisVowel(){returnfalse;}publicbooleanisConsonant(){returntrue;}publicchargetLetter(){returniTheLetter;}}// End local class Consonant}// End toplevel class Letter

Several things to note here.

  • First, you'll notice the top level classLetter is abstract. This is fine because you'll notice that it doesn't actually do anything except define the interface and provide a top level container for the two other classes. However, it's alsoimportant (not just OK) to make this abstract because we don't want people trying to instantiate theLetter class directly. Of course we could solve this problem by making a private constructor, but making the class abstract instead is cleaner, and makes it more obvious that theLetter class is notmeant to be instantiated. It also, as mentioned, allows us to define the user facing interface that the work horse classes need to implement.
  • The two nested classes we created are calledlocal classes, which is basically the same as aninner class except that local classes are static, and inner classes are not. They have to be static so that our static factory method can create them. If they were non static (i.e., dynamic) then they could only be accessed through an instance of theLetter class, which we can never have becauseLetter is abstract. Also note that (in Java, anyway) the fields for inner and local classes typically use the "i" (for inner) prefix, as opposed to the "f" (for field) prefix used by top level classes. This is simply a naming convention used by many Java programmers and doesn't actually effect the program.
  • The two nested classes that implement theLetter data type do not actually have to be local/inner. They could just have easily been top level classes that extend the abstractLetter class. However, this is contrary to the point of the factory pattern, which is encapsulation. Top level classes can't be private in Java, because that doesn't make any sense (what are they private to?) and the whole point is that no client has to (or should, really) know how the type is implemented. Making these classes top level allows clients to potentially stumble across them, and worse yet, instantiate them, by-passing the factory pattern all together.
  • Lastly, this is not very good code. There's a lot of ways we can make it better to really illustrate the power of the factory pattern. I'll discuss these refactorings briefly, and then show another, more polished, version of the above code which includes a lot of them.

Refactoring the Factory Pattern

Notice that both of the local classes do the same thing in a few places. This is redundant code which is not only more work to write, but it's also highly discouraged in object oriented programming (partiallybecause it takes more work to write, but mostly because it's harder to maintain and prone to errors, e.g., you find a bug in the code and change it in one spot, but forget to in another.) Below is a list of redundancies in the above code:

  • The fieldiTheLetter
  • The methodgetLetter()
  • The constructor of each inner class does the same thing.

In addition, as we discovered above, the isVowel() and isConsonant() just happen to always return the opposite of each other for a given instance. However, since this is something of a peculiarity for this particular example, we won't worry about it. The lesson you would learn from us doing that will already be covered in the refactoring of thegetLetter() method.OK, so we have redundant code in two classes. If you're familiar with abstracting processes, then this is probably a familiar scenario to you. Often, having redundant code in two different classes makes them prime candidates for abstraction, meaning that a new abstract class is created to implement the redundant code, and the two classes simply extend this new abstract class instead of implementing the redundant code. Well what do you know? We already have an abstract super class that our redundant classes have in common. All we have to do is make the super class implement the redundant code, and the other classes will automatically inherit this implementation, as long as we don't override it.So that works fine for thegetLetter() method, we can move both the method and theiTheLetter field up to the abstract parent class. But what about the constructors? Well our constructor takes an argument, so we won't automatically inherit it, that's just the way java works. But we can use thesuper keyword to automatically delegate to the super classes constructor. In other words, we'll implement the constructor in the super class, since that's where the field is anyway, and the other two classes will delegate to this method in their own constructors. For our example, this doesn't save much work, we're replacing a one line assignment with a one line call tosuper(), but in theory, there could be hundred of lines of code in the constructors, and moving it up could be a great help.At this point, you might be a little worried about putting a constructor in theLetter class. Didn't I already say not to do that? I thought we didn't want people trying to instantiateLetter directly? Don't worry, the class is still abstract. Even if there's a concrete constructor, Java won't let you instantiate an abstract class, because it's abstract, it could have method that are accessible but undefined, and it wouldn't know what to do if such a method was invoked. So putting the constructor in is fine.After making the above refactorings, our code now looks like this:

publicabstractclassLetter{// Factory MethodpublicstaticLettergetLetter(charaTheLetter){if(aTheLetter=='a'||aTheLetter=='e'||aTheLetter=='i'||aTheLetter=='o'||aTheLetter=='u'||aTheLetter=='A'||aTheLetter=='E'||aTheLetter=='I'||aTheLetter=='O'||aTheLetter=='U'){returnnewVowel(aTheLetter);}else{returnnewConsonant(aTheLetter);}}// Our new abstracted field. We'll make it protected so that subclasses can see it,// and we rename it from "i" to "f", following our naming convention.protectedcharfTheLetter;// Our new constructor. It can't actually be used to instantiate an instance// of Letter, but our sub classes can invoke it with superprotectedLetter(charaTheLetter){this.fTheLetter=aTheLetter;}// The new method we're abstracting up to remove redundant code in the sub classespublicchargetChar(){returnthis.fTheLetter;}// Same old abstract methods that define part of our client facing interfacepublicabstractbooleanisVowel();publicabstractbooleanisConsonant();// The local subclasses with the redundant code moved up.privatestaticclassVowelextendsLetter{// Constructor delegates to the super constructorVowel(charaTheLetter){super(aTheLetter);}// Still need to implement the abstract methodspublicbooleanisVowel(){returntrue;}publicbooleanisConsonant(){returnfalse;}}// End local class VowelprivatestaticclassConsonantextendsLetter{Consonant(charaTheLetter){super(aTheLetter);}publicbooleanisVowel(){returnfalse;}publicbooleanisConsonant(){returntrue;}}// End local class Consonant}// End toplevel class Letter

Note that we made our abstracted field protected. This isn't strictly necessary in this case, we could have left it private, because the subclasses don't actually need to access it at all. In general,I prefer to make things protected instead of private, since, as I mentioned, you can never really be sure where a project will go in the future, and you may not want to restrict future implementors (including yourself) unnecessarily. However, many people prefer to default to private and only use protected when they know it's necessary. A major reason for this is the rather peculiar and somewhat unexpected meaning ofprotected in Java, which allows not only subclasses, butanything in the same package to access it. This is a bit of a digression, but I think it's a fairly important debate that a good Java programmer should be aware of.

The Factory Pattern and Parametric Polymorphism

The version of the Java Virtual Machine 5.0 has introduced something called Parametric Polymorphism, which goes by many other names in other languages, including "generic typing" in C++. In order to really understand the rest of this section, you should read that section first. But basically, this means that you can introduce additional parameters into a class—parameters which are set at instantiation—that define the types of certain elements in the class, for instance fields or method return values. This is a very powerful tool which allows programmers to avoid a lot of those nastyinstanceofs and narrowing castes. However, the implementation of this device in the JVM does not promote the use of the Factory pattern, and in the two do not play well together. This is because Java does not allow methods to be parameterized the way types are, so you cannot dynamically parameterize an instance through a method, only through use of thenew operator. As an example, imagine a typeFoo which is parameterized with a single type which we'll callT. In java we would write this class like this:

classFoo<T>{}// End class Foo

Now we can have instances ofFoo parameterized by all sorts of types, for instance:

Foo<String>fooOverString=newFoo<String>();Foo<Integer>fooOverInteger=newFoo<Integer>();

But let's say we want to use the factory pattern forFoo. How do we do that? You could create a different factory method for each type you want to parameterize over, for instance:

classFoo<T>{staticFoo<String>getFooOverString(){returnnewFoo<String>();}staticFoo<Integer>getFooOverInteger(){returnnewFoo<Integer>();}}// End class Foo

But what about something like theArrayList class (in the java.util package)? In the java standard libraries released with 5.0, ArrayList is parameterized to define the type of the object stored in it. We certainly don't want to restrict what kinds of types it can be parameterized with by having to write a factory method for each type. This is often the case with parameterized types: you don't know what types users will want to parameterize with, and you don't want to restrict them, so the factory pattern won't work for that.You are allowed to instantiate a parameterized type in a generic form, meaning you don't specify the parameter at all, you just instantiate it the way you would have before 5.0. But that forces you to give up the parameterization.This is how you do it with generics:

classFoo<T>{publicstatic<E>Foo<E>getFoo(){returnnewFoo<E>();}}// End class Foo

Examples

In Java, a class that implementsjava.sql.Connection is a factory of statements. By calling thecreateStatement() method, you create a statement for which you only know the interface. The factory chooses the right instance class for you.

Cost

This pattern is not so expensive when it is implemented at the right time. It can be more expensive if you have to refactor an existing code.

Creation

Its implementation is easy and there is no additional cost (it is not more expensive than an implementation without this pattern).

Maintenance

There is no additional cost nor additional constraint.

Removal

This pattern can be easily removed as automatic refactoring operations can easily remove its existence.

Good practices

  • Name the factory class with the instanciated class in prefix and thefactory term in suffix, to indicate the use of the pattern to the other developers.
  • Avoid to persist the factory result (in database) into the factory class, to respect the SOLID principles and allow the creation of ephemeral objects, for example into the automatic tests.

Implementation

Implementation in ABAP
REPORTzz_pizza_factory_testNOSTANDARDPAGEHEADING.TYPESty_pizza_typeTYPEi.*----------------------------------------------------------------------**       CLASS lcl_pizza DEFINITION*----------------------------------------------------------------------*CLASSlcl_pizzaDEFINITIONABSTRACT.PUBLIC SECTION.DATAp_pizza_nameTYPEstring.METHODSget_priceABSTRACTRETURNINGvalue(y_price)TYPEi.ENDCLASS."lcl_pizza DEFINITION*----------------------------------------------------------------------**       CLASS lcl_ham_and_mushroom_pizza DEFINITION*----------------------------------------------------------------------*CLASSlcl_ham_and_mushroom_pizzaDEFINITIONINHERITING FROMlcl_pizza.PUBLIC SECTION.METHODSconstructor.METHODSget_priceREDEFINITION.ENDCLASS."lcl_ham_and_mushroom_pizza DEFINITION*----------------------------------------------------------------------**       CLASS lcl_deluxe_pizza DEFINITION*----------------------------------------------------------------------*CLASSlcl_deluxe_pizzaDEFINITIONINHERITING FROMlcl_pizza.PUBLIC SECTION.METHODSconstructor.METHODSget_priceREDEFINITION.ENDCLASS."lcl_ham_and_mushroom_pizza DEFINITION*----------------------------------------------------------------------**       CLASS lcl_hawaiian_pizza DEFINITION*----------------------------------------------------------------------*CLASSlcl_hawaiian_pizzaDEFINITIONINHERITING FROMlcl_pizza.PUBLIC SECTION.METHODSconstructor.METHODSget_priceREDEFINITION.ENDCLASS."lcl_ham_and_mushroom_pizza DEFINITION*----------------------------------------------------------------------**       CLASS lcl_pizza_factory DEFINITION*----------------------------------------------------------------------*CLASSlcl_pizza_factoryDEFINITION.PUBLIC SECTION.CONSTANTS:BEGIN OFco_pizza_type,ham_mushroomTYPEty_pizza_typeVALUE1,deluxeTYPEty_pizza_typeVALUE2,hawaiianTYPEty_pizza_typeVALUE3,END OFco_pizza_type.CLASS-METHODScreate_pizzaIMPORTINGx_pizza_typeTYPEty_pizza_typeRETURNINGvalue(yo_pizza)TYPE REF TOlcl_pizzaEXCEPTIONSex_invalid_pizza_type.ENDCLASS."lcl_pizza_factory DEFINITION*----------------------------------------------------------------------**       CLASS lcl_ham_and_mushroom_pizza*----------------------------------------------------------------------*CLASSlcl_ham_and_mushroom_pizzaIMPLEMENTATION.METHODconstructor.super->constructor().p_pizza_name='Ham & Mushroom Pizza'(001).ENDMETHOD."constructorMETHODget_price.y_price=850.ENDMETHOD."get_priceENDCLASS."lcl_ham_and_mushroom_pizza IMPLEMENTATION*----------------------------------------------------------------------**       CLASS lcl_deluxe_pizza IMPLEMENTATION*----------------------------------------------------------------------*CLASSlcl_deluxe_pizzaIMPLEMENTATION.METHODconstructor.super->constructor().p_pizza_name='Deluxe Pizza'(002).ENDMETHOD."constructorMETHODget_price.y_price=1050.ENDMETHOD."get_priceENDCLASS."lcl_deluxe_pizza IMPLEMENTATION*----------------------------------------------------------------------**       CLASS lcl_hawaiian_pizza IMPLEMENTATION*----------------------------------------------------------------------*CLASSlcl_hawaiian_pizzaIMPLEMENTATION.METHODconstructor.super->constructor().p_pizza_name='Hawaiian Pizza'(003).ENDMETHOD."constructorMETHODget_price.y_price=1150.ENDMETHOD."get_priceENDCLASS."lcl_hawaiian_pizza IMPLEMENTATION*----------------------------------------------------------------------**       CLASS lcl_pizza_factory IMPLEMENTATION*----------------------------------------------------------------------*CLASSlcl_pizza_factoryIMPLEMENTATION.METHODcreate_pizza.CASEx_pizza_type.WHENco_pizza_type-ham_mushroom.CREATE OBJECTyo_pizzaTYPElcl_ham_and_mushroom_pizza.WHENco_pizza_type-deluxe.CREATE OBJECTyo_pizzaTYPElcl_deluxe_pizza.WHENco_pizza_type-hawaiian.CREATE OBJECTyo_pizzaTYPElcl_hawaiian_pizza.ENDCASE.ENDMETHOD."create_pizzaENDCLASS."lcl_pizza_factory IMPLEMENTATIONSTART-OF-SELECTION.DATAgo_pizzaTYPE REF TOlcl_pizza.DATAlv_priceTYPEi.DO3TIMES.go_pizza=lcl_pizza_factory=>create_pizza(sy-index).lv_price=go_pizza->get_price().WRITE:/'Price of',go_pizza->p_pizza_name,'is £',lv_priceLEFT-JUSTIFIED.ENDDO.*Output:*Price of Ham & Mushroom Pizza is £ 850*Price of Deluxe Pizza is £ 1.050*Price of Hawaiian Pizza is £ 1.150
Implementation in ActionScript 3.0
publicclassPizza{protectedvar_price:Number;publicfunctiongetprice():Number{return_price;}}publicclassHamAndMushroomPizzaextendsPizza{publicfunctionHamAndMushroomPizza(){_price=8.5;}}publicclassDeluxePizzaextendsPizza{publicfunctionDeluxePizza(){_price=10.5;}}publicclassHawaiianPizzaextendsPizza{publicfunctionHawaiianPizza(){_price=11.5;}}publicclassPizzaFactory{staticpublicfunctioncreatePizza(type:String):Pizza{switch(type){case"HamAndMushroomPizza":returnnewHamAndMushroomPizza();break;case"DeluxePizza":returnnewDeluxePizza();break;case"HawaiianPizza":returnnewHawaiianPizza();break;default:thrownewArgumentError("The pizza type "+type+" is not recognized.");}}}publicclassMainextendsSprite{publicfunctionMain(){foreach(varpizza:Stringin["HamAndMushroomPizza","DeluxePizza","HawaiianPizza"]){trace("Price of "+pizza+" is "+PizzaFactory.createPizza(pizza).price);}}}Output:PriceofHamAndMushroomPizzais8.5PriceofDeluxePizzais10.5PriceofHawaiianPizzais11.5
Implementation in C++

This C++11 implementation is based on the pre C++98 implementation in the book.

#include<iostream>enumDirection{North,South,East,West};classMapSite{public:virtualvoidenter()=0;virtual~MapSite()=default;};classRoom:publicMapSite{public:Room():roomNumber(0){}Room(intn):roomNumber(n){}voidsetSide(Directiond,MapSite*ms){std::cout<<"Room::setSide "<<d<<' '<<ms<<'\n';}virtualvoidenter(){}Room(constRoom&)=delete;// rule of threeRoom&operator=(constRoom&)=delete;private:introomNumber;};classWall:publicMapSite{public:Wall(){}virtualvoidenter(){}};classDoor:publicMapSite{public:Door(Room*r1=nullptr,Room*r2=nullptr):room1(r1),room2(r2){}virtualvoidenter(){}Door(constDoor&)=delete;// rule of threeDoor&operator=(constDoor&)=delete;private:Room*room1;Room*room2;};classMaze{public:voidaddRoom(Room*r){std::cout<<"Maze::addRoom "<<r<<'\n';}Room*roomNo(int)const{returnnullptr;}};// If createMaze calls virtual functions instead of constructor calls to create the rooms, walls, and doors it requires, then you can change the classes that get instantiated by making a subclass of MazeGame and redefining those virtual functions. This approach is an example of the Factory Method (121) pattern.classMazeGame{public:Maze*createMaze(){Maze*aMaze=makeMaze();Room*r1=makeRoom(1);Room*r2=makeRoom(2);Door*theDoor=makeDoor(r1,r2);aMaze->addRoom(r1);aMaze->addRoom(r2);r1->setSide(North,makeWall());r1->setSide(East,theDoor);r1->setSide(South,makeWall());r1->setSide(West,makeWall());r2->setSide(North,makeWall());r2->setSide(East,makeWall());r2->setSide(South,makeWall());r2->setSide(West,theDoor);returnaMaze;}// factory methods:virtualMaze*makeMaze()const{returnnewMaze;}virtualRoom*makeRoom(intn)const{returnnewRoom(n);}virtualWall*makeWall()const{returnnewWall;}virtualDoor*makeDoor(Room*r1,Room*r2)const{returnnewDoor(r1,r2);}virtual~MazeGame()=default;};intmain(){MazeGamegame;game.createMaze();}

The program output is like:

Maze::addRoom0xcaced0Maze::addRoom0xcacef0Room::setSide00xcad340Room::setSide20xcacf10Room::setSide10xcad360Room::setSide30xcad380Room::setSide00xcad3a0Room::setSide20xcad3c0Room::setSide10xcad3e0Room::setSide30xcacf10
Implementation in Common Lisp

InCommon Lisp, factory methods are not really needed, because classes and class names are first class values.

(defclasspizza()((price:accessorprice)))(defclassham-and-mushroom-pizza(pizza)((price:initform850)))(defclassdeluxe-pizza(pizza)((price:initform1050)))(defclasshawaiian-pizza(pizza)((price:initform1150)))(defparameter*pizza-types*(list'ham-and-mushroom-pizza'deluxe-pizza'hawaiian-pizza))(loopforpizza-typein*pizza-types*do(formatt"~%Price of ~a is ~a"pizza-type(price(make-instancepizza-type))))Output:PriceofHAM-AND-MUSHROOM-PIZZAis850PriceofDELUXE-PIZZAis1050PriceofHAWAIIAN-PIZZAis1150
Implementation in Delphi
programFactoryMethod;{$APPTYPE CONSOLE}usesSysUtils;type// ProductTProduct=class(TObject)publicfunctionGetName():string;virtual;abstract;end;// ConcreteProductATConcreteProductA=class(TProduct)publicfunctionGetName():string;override;end;// ConcreteProductBTConcreteProductB=class(TProduct)publicfunctionGetName():string;override;end;// CreatorTCreator=class(TObject)publicfunctionFactoryMethod():TProduct;virtual;abstract;end;// ConcreteCreatorATConcreteCreatorA=class(TCreator)publicfunctionFactoryMethod():TProduct;override;end;// ConcreteCreatorBTConcreteCreatorB=class(TCreator)publicfunctionFactoryMethod():TProduct;override;end;{ ConcreteProductA }functionTConcreteProductA.GetName():string;beginResult:='ConcreteProductA';end;{ ConcreteProductB }functionTConcreteProductB.GetName():string;beginResult:='ConcreteProductB';end;{ ConcreteCreatorA }functionTConcreteCreatorA.FactoryMethod():TProduct;beginResult:=TConcreteProductA.Create();end;{ ConcreteCreatorB }functionTConcreteCreatorB.FactoryMethod():TProduct;beginResult:=TConcreteProductB.Create();end;constCount=2;varCreators:array[1..Count]ofTCreator;Product:TProduct;I:Integer;begin// An array of creatorsCreators[1]:=TConcreteCreatorA.Create();Creators[2]:=TConcreteCreatorB.Create();// Iterate over creators and create productsforI:=1toCountdobeginProduct:=Creators[I].FactoryMethod();WriteLn(Product.GetName());Product.Free();end;forI:=1toCountdoCreators[I].Free();ReadLn;end.
Implementation in Java

Example with pizza

abstractclassPizza{publicabstractintgetPrice();// Count the cents}
classHamAndMushroomPizzaextendsPizza{publicintgetPrice(){return850;}}
classDeluxePizzaextendsPizza{publicintgetPrice(){return1050;}}
classHawaiianPizzaextendsPizza{publicintgetPrice(){return1150;}}
classPizzaFactory{publicenumPizzaType{HamMushroom,Deluxe,Hawaiian}publicstaticPizzacreatePizza(PizzaTypepizzaType){switch(pizzaType){caseHamMushroom:returnnewHamAndMushroomPizza();caseDeluxe:returnnewDeluxePizza();caseHawaiian:returnnewHawaiianPizza();}thrownewIllegalArgumentException("The pizza type "+pizzaType+" is not recognized.");}}
classPizzaLover{/**     * Create all available pizzas and print their prices     */publicstaticvoidmain(Stringargs[]){for(PizzaFactory.PizzaTypepizzaType:PizzaFactory.PizzaType.values()){System.out.println("Price of "+pizzaType+" is "+PizzaFactory.createPizza(pizzaType).getPrice());}}}

Output:

Price of HamMushroom is 850Price of Deluxe is 1050Price of Hawaiian is 1150

Another example with image

Abstract creator
Interface to create the Product.
packagemypkg;importjava.awt.image.BufferedImage;importjava.io.IOException;/** * * @author xxx */publicinterfacePhotoReader{publicBufferedImagegetImage()throwsIOException;}
Concrete creator
a class to create specific Product.
packagemypkg;importjava.awt.image.BufferedImage;importjava.io.File;importjava.io.IOException;importjava.util.Iterator;importjavax.imageio.ImageIO;importjavax.imageio.ImageReader;importjavax.imageio.stream.ImageInputStream;/** * * @author xxx */publicclassJPEGReaderimplementsPhotoReader{ImageReaderreader;FilejpegFile;ImageInputStreamiis;publicJPEGReader(StringfilePath)throwsIOException{jpegFile=newFile(filePath);iis=ImageIO.createImageInputStream(jpegFile);Iteratorreaders=ImageIO.getImageReadersByFormatName("jpg");reader=(ImageReader)readers.next();this.reader.setInput(iis,true);}publicBufferedImagegetImage()throwsIOException{returnreader.read(0);}
Factory class
a class to return a specific concrete creator at runtime to create the product.
packagemypkg;importjava.io.IOException;/** * * @author xxx */publicclassPhotoReaderFactory{enumMimi{jpg,JPG,gif,GIF,bmp,BMP,png,PNG};publicstaticPhotoReadergetPhotoReader(StringfilePath){Stringsuffix=getFileSuffix(filePath);PhotoReaderreader=null;try{switch(Mimi.valueOf(suffix)){casejpg:caseJPG:reader=newJPEGReader(filePath);break;casegif:caseGIF:reader=newGIFReader(filePath);break;casebmp:caseBMP:reader=newBMPReader(filePath);break;casepng:casePNG:reader=newPNGReader(filePath);break;default:break;}}catch(IOExceptionio){io.printStackTrace();}returnreader;}privatestaticStringgetFileSuffix(StringfilePath){String[]stringArray=filePath.split("\\.");returnstringArray[stringArray.length-1];}}
Implementation in Javascript

This example inJavaScript usesFirebug console to output information.

/** * Extends parent class with child. In Javascript, the keyword "extends" is not * currently implemented, so it must be emulated. * Also it is not recommended to use keywords for future use, so we name this * function "extends" with capital E. Javascript is case-sensitive. * * @param  function  parent constructor function * @param  function  (optional) used to override default child constructor function */functionExtends(parent,childConstructor){varF=function(){};F.prototype=parent.prototype;varChild=childConstructor||function(){};Child.prototype=newF();Child.prototype.constructor=Child;Child.parent=parent.prototype;// return instance of new objectreturnChild;}/** * Abstract Pizza object constructor */functionPizza(){thrownewError('Cannot instatiate abstract object!');}Pizza.prototype.price=0;Pizza.prototype.getPrice=function(){returnthis.price;}varHamAndMushroomPizza=Extends(Pizza);HamAndMushroomPizza.prototype.price=8.5;varDeluxePizza=Extends(Pizza);DeluxePizza.prototype.price=10.5;varHawaiianPizza=Extends(Pizza);HawaiianPizza.prototype.price=11.5;varPizzaFactory={createPizza:function(type){varbaseObject='Pizza';vartargetObject=type.charAt(0).toUpperCase()+type.substr(1);if(typeofwindow[targetObject+baseObject]==='function'){returnnewwindow[targetObject+baseObject];}else{thrownewError('The pizza type '+type+' is not recognized.');}}};//var price = PizzaFactory.createPizza('deluxe').getPrice();varpizzas=['HamAndMushroom','Deluxe','Hawaiian'];for(variinpizzas){console.log('Price of '+pizzas[i]+' is '+PizzaFactory.createPizza(pizzas[i]).getPrice());}

Output

Price of HamAndMushroom is 8.50Price of Deluxe is 10.50Price of Hawaiian is 11.50
Implementation in Haskell
classPizzaawhereprice::a->FloatdataHamMushroom=HamMushroomdataDeluxe=DeluxedataHawaiian=HawaiianinstancePizzaHamMushroomwhereprice_=8.50instancePizzaDeluxewhereprice_=10.50instancePizzaHawaiianwhereprice_=11.50

Usage example:

main=print(priceHawaiian)
Implementation in Perl
packagePizza;useMoose;hasprice=>(is=>"rw",isa=>"Num",builder=>"_build_price");packageHamAndMushroomPizza;useMoose;extends"Pizza";sub_build_price{8.5}packageDeluxePizza;useMoose;extends"Pizza";sub_build_price{10.5}packageHawaiianPizza;useMoose;extends"Pizza";sub_build_price{11.5}packagePizzaFactory;subcreate{my($self,$type)=@_;return($type."Pizza")->new;}packagemain;formy$type(qw( HamAndMushroom Deluxe Hawaiian )){printf"Price of %s is %.2f\n",$type,PizzaFactory->create($type)->price;}

Factories are not really needed for this example in Perl, and this may be written more concisely:

packagePizza;useMoose;hasprice=>(is=>"rw",isa=>"Num",builder=>"_build_price");packageHamAndMushroomPizza;useMoose;extends"Pizza";sub_build_price{8.5}packageDeluxePizza;useMoose;extends"Pizza";sub_build_price{10.5}packageHawaiianPizza;useMoose;extends"Pizza";sub_build_price{11.5}packagemain;formy$type(qw( HamAndMushroom Deluxe Hawaiian )){printf"Price of %s is %.2f\n",$type,($type."Pizza")->new->price;}

Output

Price of HamAndMushroom is 8.50Price of Deluxe is 10.50Price of Hawaiian is 11.50
Implementation in PHP
<?phpabstractclassPizza{protected$_price;publicfunctiongetPrice(){return$this->_price;}}classHamAndMushroomPizzaextendsPizza{protected$_price=8.5;}classDeluxePizzaextendsPizza{protected$_price=10.5;}classHawaiianPizzaextendsPizza{protected$_price=11.5;}classPizzaFactory{publicstaticfunctioncreatePizza($type){$baseClass='Pizza';$targetClass=ucfirst($type).$baseClass;if(class_exists($targetClass)&&is_subclass_of($targetClass,$baseClass))returnnew$targetClass;elsethrownewException("The pizza type '$type' is not recognized.");}}$pizzas=array('HamAndMushroom','Deluxe','Hawaiian');foreach($pizzasas$p){printf("Price of %s is %01.2f".PHP_EOL,$p,PizzaFactory::createPizza($p)->getPrice());}// Output:// Price of HamAndMushroom is 8.50// Price of Deluxe is 10.50// Price of Hawaiian is 11.50?>
Implementation in Python
## Pizza#classPizza(object):def__init__(self):self._price=Nonedefget_price(self):returnself._priceclassHamAndMushroomPizza(Pizza):def__init__(self):self._price=8.5classDeluxePizza(Pizza):def__init__(self):self._price=10.5classHawaiianPizza(Pizza):def__init__(self):self._price=11.5## PizzaFactory#classPizzaFactory(object):@staticmethoddefcreate_pizza(pizza_type):ifpizza_type=='HamMushroom':returnHamAndMushroomPizza()elifpizza_type=='Deluxe':returnDeluxePizza()elifpizza_type=='Hawaiian':returnHawaiianPizza()if__name__=='__main__':forpizza_typein('HamMushroom','Deluxe','Hawaiian'):print'Price of{0} is{1}'.format(pizza_type,PizzaFactory.create_pizza(pizza_type).get_price())

As in Perl, Common Lisp and other dynamic languages, factories of the above sort aren't really necessary, since classes are first-class objects and can be passed around directly, leading to this more natural version:

## Pizza#classPizza(object):def__init__(self):self._price=Nonedefget_price(self):returnself._priceclassHamAndMushroomPizza(Pizza):def__init__(self):self._price=8.5classDeluxePizza(Pizza):def__init__(self):self._price=10.5classHawaiianPizza(Pizza):def__init__(self):self._price=11.5if__name__=='__main__':forpizza_classin(HamAndMushroomPizza,DeluxePizza,HawaiianPizza):print('Price of{0} is{1}'.format(pizza_class.__name__,pizza_class().get_price()))

Note in the above that the classes themselves are simply used as values, whichpizza_class iterates over. The class gets created simply by treating it as a function. In this case, ifpizza_class holds a class, thenpizza_class() creates a new object of that class.Another way of writing the final clause, which sticks more closely to the original example and uses strings instead of class objects, is as follows:

if__name__=='__main__':forpizza_typein('HamAndMushroom','Deluxe','Hawaiian'):print'Price of{0} is{1}'.format(pizza_type,eval(pizza_type+'Pizza')().get_price())

In this case, the correct class name is constructed as a string by adding'Pizza', andeval is called to turn it into a class object.

Implementation in VB.NET
ImportsSystemNamespaceFactoryMethodPatternPublicClassProgramSharedSubMain()OutputPizzaFactory(NewLousPizzaStore())OutputPizzaFactory(NewTonysPizzaStore())Console.ReadKey()EndSubPrivateSharedSubOutputPizzaFactory(ByValfactoryAsIPizzaFactory)Console.WriteLine("Welcome to {0}",factory.Name)ForEachpAsPizzaInfactory.CreatePizzasConsole.WriteLine("  {0} - ${1} - {2}",p.GetType().Name,p.Price,p.Toppings)NextEndSubEndClassPublicMustInheritClassPizzaProtected_toppingsAsStringProtected_priceAsDecimalPublicReadOnlyPropertyToppings()AsStringGetReturn_toppingsEndGetEndPropertyPublicReadOnlyPropertyPrice()AsDecimalGetReturn_priceEndGetEndPropertyPublicSubNew(ByVal__priceAsDecimal)_price=__priceEndSubEndClassPublicInterfaceIPizzaFactoryReadOnlyPropertyName()AsStringFunctionCreatePizzas()AsPizza()EndInterfacePublicClassPepperoniInheritsPizzaPublicSubNew(ByValpriceAsDecimal)MyBase.New(price)_toppings="Cheese, Pepperoni"EndSubEndClassPublicClassCheeseInheritsPizzaPublicSubNew(ByValpriceAsDecimal)MyBase.New(price)_toppings="Cheese"EndSubEndClassPublicClassLousSpecialInheritsPizzaPublicSubNew(ByValpriceAsDecimal)MyBase.New(price)_toppings="Cheese, Pepperoni, Ham, Lou's Special Sauce"EndSubEndClassPublicClassTonysSpecialInheritsPizzaPublicSubNew(ByValpriceAsDecimal)MyBase.New(price)_toppings="Cheese, Bacon, Tomatoes, Tony's Special Sauce"EndSubEndClassPublicClassLousPizzaStoreImplementsIPizzaFactoryPublicFunctionCreatePizzas()AsPizza()ImplementsIPizzaFactory.CreatePizzasReturnNewPizza(){NewPepperoni(6.99D),NewCheese(5.99D),NewLousSpecial(7.99D)}EndFunctionPublicReadOnlyPropertyName()AsStringImplementsIPizzaFactory.NameGetReturn"Lou's Pizza Store"EndGetEndPropertyEndClassPublicClassTonysPizzaStoreImplementsIPizzaFactoryPublicFunctionCreatePizzas()AsPizza()ImplementsIPizzaFactory.CreatePizzasReturnNewPizza(){NewPepperoni(6.5D),NewCheese(5.5D),NewTonysSpecial(7.5D)}EndFunctionPublicReadOnlyPropertyName()AsStringImplementsIPizzaFactory.NameGetReturn"Tony's Pizza Store"EndGetEndPropertyEndClassEndNamespaceOutput:WelcometoLou's Pizza StorePepperoni-$6.99-Cheese,PepperoniCheese-$5.99-CheeseLousSpecial-$7.99-Cheese,Pepperoni,Ham,Lou's Special SauceWelcometoTony's Pizza StorePepperoni-$6.5-Cheese,PepperoniCheese-$5.5-CheeseTonysSpecial-$7.5-Cheese,Bacon,Tomatoes,Tony's Special Sauce


Clipboard

To do:
Add more illustrations.


FacadeComputer Science Design Patterns
Factory method
Flyweight


You have questions about this page?
Ask it here:


Create a new page on this book:


Retrieved from "https://en.wikibooks.org/w/index.php?title=Computer_Science_Design_Patterns/Factory_method&oldid=4416745"
Category:
Hidden category:

[8]ページ先頭

©2009-2025 Movatter.jp