| Computer Science Design Patterns Factory method | Flyweight |
The factory pattern is a design pattern used to promote encapsulation of data representation.
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.
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.
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:
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 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
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.
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.
Its implementation is easy and there is no additional cost (it is not more expensive than an implementation without this pattern).
There is no additional cost nor additional constraint.
This pattern can be easily removed as automatic refactoring operations can easily remove its existence.
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
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
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
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
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.
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
packagemypkg;importjava.awt.image.BufferedImage;importjava.io.IOException;/** * * @author xxx */publicinterfacePhotoReader{publicBufferedImagegetImage()throwsIOException;}
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);}
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];}}
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
classPizzaawhereprice::a->FloatdataHamMushroom=HamMushroomdataDeluxe=DeluxedataHawaiian=HawaiianinstancePizzaHamMushroomwhereprice_=8.50instancePizzaDeluxewhereprice_=10.50instancePizzaHawaiianwhereprice_=11.50
Usage example:
main=print(priceHawaiian)
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
<?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?>
## 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.
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
To do: |
| Computer Science Design Patterns Factory method | Flyweight |