Encapsulating Creation

xiaoxiao2021-03-06  85

Encapsulating creationWhen you discover that you need to add new types to a system, the most sensible first step is to use polymorphism to create a common interface to those new types. This separates the rest of the code in your system from the knowledge of the specific types that you are adding. New types may be added without disturbing existing code ... or so it seems. At first it would appear that the only place you need to change the code in such a design is the place where you inherit a new type, but this is not quite true. you must still create an object of your new type, and at the point of creation you must specify the exact constructor to use. Thus, if the code that creates objects is distributed throughout your application, you have the same problem when adding new types-you must still chase down all the points of your code where type matters. It happens to be the creation of the type that matters in this case rather than the use of the type (which is taken care of by POLYMORPH ISM), But The Effect Is The Same: Adding A New Type Can Cause Problems.

The solution is to force the creation of objects to occur through a common factory rather than to allow the creational code to be spread throughout your system. If all the code in your program must go through this factory whenever it needs to create one of your objects . t................

Since every object-oriented program creates objects, and since it's very likely you will extend your program by adding new types, I suspect that factories may be the most universally useful kinds of design patterns.

Although only the Simple Factory Method is a true singleton, you'll find that each specify factory class in the more general types of factories will only have a single instance.Simple Factory methodAs an example, let's revisit the Shape system.

One Approach Is To Make The Factory A Static Method of The Base Class:

//: Factory: ShapeFact1: ShapeFactory1.java

// a Simple Static Factory Method.

Package factory.shapefact1;

Import java.util. *;

Import junit.framework. *;

Abstract class shape {

Public Abstract void Draw ();

Public abstract void erases;

Public Static Shape Factory (String Type) {

IF (Type.equals ("Circle")) Return New Circle ();

IF (Type.equals ("Square")) Return New Square ();

Throw new runtimeException

"Bad Shape Creation:" TYPE);

}

}

Class Circle Extends Shape {

Circle () {} // package-access constructor

Public void draw () {

System.out.println ("Circle.Draw");

}

Public void erase () {

System.out.println ("Circle.ed");

}

}

Class Square Extends Shape {

Square () {} // package-access constructor

Public void draw () {

System.out.println ("Square.Draw");

}

Public void erase () {

System.out.println ("Square.rase");

}

}

Public class shapefactory1 extends testcase {

String shlist [] = {"circle", "Square",

"Square", "Circle", "Circle", "Square"};

List shapes = new arraylist ();

Public void test () {

Iterator it = arrays.aslist (shlist) .Itemrator ();

While (it.hasnext ())

Shapes.add (Shape.Factory ((String) IT.NEXT ());

IT = shapes.iterator ();

While (it.hasnext ()) {

Shape s = (shape) it.next ();

s.draw ();

S.RASE ();

}

}

Public static void main (string args []) {

Junit.textui.teStrunner.Run (ShapeFactory1.class);

} ///: ~

The factory () takes an argument that allows it to determine what type of Shape to create;. It happens to be a String in this case but it could be any set of data The factory () is now the only other code in the system that needs to be changed when a new type of Shape is added (the initialization data for the objects will presumably come from somewhere outside the system, and not be a hard-coded array as in the above example).

To encourage creation to only happen in the factory (), the constructors for the specific types of Shape are give package access, so factory () has access to the constructors but they are not available outside the package.

Polymorphic factoriesThe static factory () method in the previous example forces all the creation operations to be focused in one spot, so that's the only place you need to change the code. This is certainly a reasonable solution, as it throws a box around the process of creating objects. However, the Design Patterns book emphasizes that the reason for the Factory Method pattern is so that different types of factories can be subclassed from the basic factory (the above design is mentioned as a special case). However, the book does not provide an example, but instead just repeats the example used for the Abstract Factory (you'll see an example of this in the next section). Here is ShapeFactory1.java modified so the factory methods are in a separate class as virtual functions. NOTICE Also That The Specific Shape Classes Are Dynamically Loaded on Demand:

//: Factory: Shapefact2: ShapeFactory2.java

// Polymorphic Factory Methods.

Package factory.shapefact2;

Import java.util. *;

Import junit.framework. *;

Interface shape {void Draw ();

void erases;

}

Abstract class shapefactory {

Protected abstract shape create ();

Private static map factories = new hashmap ();

Public static void

AddFactory (String ID, ShapeFactory f) {

Factories.put (ID, f);

}

// a Template Method:

Public Static Final

Shape createShape (String ID) {

IF (! Factories.Containskey (ID)) {

Try {

// load dynamically

Class.Forname ("Factory.ShapeFact2." ID);

} catch (classnotfoundexception e) {

Throw new runtimeException

"Bad Shape Creation:" ID);

}

// See if it is put in:

IF (! Factories.ContainsKey (ID))

Throw new runtimeException

"Bad Shape Creation:" ID);

}

Return

(ShapeFactory) Factories.get (ID)). CREATE ();

}

}

Class Circle Implements Shape {

private circle () {}

Public void draw () {

System.out.println ("Circle.Draw");

}

Public void erase () {

System.out.println ("Circle.ed");

}

Private static class factory

Extends shapefactory {

Protected Shape Create () {

Return new circle ();

}

}

STATIC {

ShapeFactory.AddFactory (

"Circle", new factory ());

}

}

Class Square Implements Shape {

Private square () {}

Public void draw () {

System.out.println ("Square.Draw");

}

Public void erase () {

System.out.println ("Square.rase");

}

Private static class factory

Extends shapefactory {

Protected Shape Create () {

Return new Square ();

}

}

STATIC {

ShapeFactory.AddFactory (

"Square", new factory ());

}

}

Public Class ShapeFactory2 Extends Testcase {

String shlist [] = {"circle", "Square",

"Square", "Circle", "Circle", "Square"};

List shapes = new arraylist ();

Public void test () {// this Just Makes Sure IT Will Complete

// WITHOUT THROWING AN Exception.

Iterator it = arrays.aslist (shlist) .Itemrator ();

While (it.hasnext ())

Shapes.add

ShapeFactory.createshape (String) IT.Next ()));

IT = shapes.iterator ();

While (it.hasnext ()) {

Shape s = (shape) it.next ();

s.draw ();

S.RASE ();

}

}

Public static void main (string args []) {

JUnit.textui.teStrunner.run (ShapeFactory2.class);

}

} ///: ~

Now the factory method appears in its own class, ShapeFactory, as the create () method. This is a protected method which means it can not be called directly, but it can be overridden. The subclasses of Shape must each create their own subclasses of ShapeFactory and override the create () method to create an object of their own type. The actual creation of shapes is performed by calling ShapeFactory.createShape (), which is a static method that uses the Map in ShapeFactory to find the appropriate factory object based on an identifier that you pass it. The factory is immediately used to create the shape object, but you could imagine a more complex problem where the appropriate factory object is returned and then used by the caller to create an object in a more sophisticated way. However , IT Seems That Much of the Time You Don't Need The Intricacies of The Polymorphic Factory Method, And A Single Static Method In The Base Class (As Shown In Shapefactory1.java) Will Work Fine.

Notice that the ShapeFactory must be initialized by loading its Map with factory objects, which takes place in the static initialization clause of each of the Shape implementations. So to add a new type to this design you must inherit the type, create a factory, and add the static initialization clause to load the Map. This extra complexity again suggests the use of a static factory method if you do not need to create individual factory objects.Abstract pattern looks factoriesThe Abstract Factory like the factory objects we've seen previously, with not one but several factory methods. Each of the factory methods creates a different kind of object. The idea is that at the point of creation of the factory object, you decide how all the objects created by that factory will be used. The example Given in Design Patterns Implements Portability Across Various Graphical User Interfaces (Guis): You Create a factory Object Appropriate to the Gui That You're Working with, And from Then on When You Ask it for a menu, button, slider, etc. It will automaticly create the appropriate version of what item for the gui. thus you're able to isolate, in One Place, The Effect of Changing from ONE GUI To ANOTHER.

As Another Example Suppose You Are Creating A General-Purpose Gaming Environment and You Want To Be Able To Support Different Types of Games. Here's How IT Might Look Using An Abstract Factory:

//: Factory: Games.java

// an Example of the Abstract Factory Pattern.

Package factory;

Import junit.framework. *;

Interface Obstacle {

Void Action ();

}

Interface Player {

Void InteractWith (OBSTACLE O);

}

Class Kitty Implements Player {

Public void interactwith (Obstacle ob) {

System.out.print ("Kitty Has Encountered A";

Ob.action ();

}

Class Kungfuguy Implements Player {

Public void interactwith (Obstacle ob) {

System.out.print ("Kungfuguy Now Battles A);

ob. activity ();

}

}

Class Puzzle IMPLEments OBSTACLE {

Public void action () {

System.out.println ("puzzle");

}

}

Class Nastyweapon Implements Obstacle {

Public void action () {

System.out.println ("NastyWeapon");

}

}

// The Abstract Factory:

Interface GameElementFactory {

Player makeplayer ();

Obstacle makeobstacle ();

}

// Concrete Factories:

Class Kittiesandpuzzles

Implements GameElementFactory {

Public Player makeplayer () {

Return new kitty ();

}

Public Obstacle makeobstacle () {

Return New Puzzle ();

}

}

Class KillandDismember

Implements GameElementFactory {

Public Player makeplayer () {

Return new kungfuguy ();

}

Public Obstacle makeobstacle () {

Return new NastyWeapon ();

}

}

Class GameEnvironment {

Private gameElementFactory Gef;

Private Player P;

Private Obstacle OB;

Public GameEnvironment

GameElementFactory Factory) {

GEF = Factory;

P = factory.makeplayer ();

Ob = factory.makeobstacle ();

}

Public void play () {p.interactwith (ob);

}

Public Class Games Extends Testcase {

GameElementFactory

KP = new kittiesandpuzzles (),

Kd = new killanddismember ();

GameEnvironment

G1 = New GameEnvironment (KP),

G2 = New GameEnvironment (kd);

// Thase Just Ensure No Exceptions Are Thrown:

Public void test1 () {g1.play ();

Public void test2 () {g2.play ();

Public static void main (string args []) {

Junit.textui.teStrunner.Run (Games.class);

}

} ///: ~

In this environment, Player objects interact with Obstacle objects, but there are different types of players and obstacles depending on what kind of game you're playing. You determine the kind of game by choosing a particular GameElementFactory, and then the GameEnvironment controls the setup and play of the game. In this example, the setup and play is very simple, but those activities (the initial conditions and the state change) can determine much of the game's outcome. Here, GameEnvironment is not designed to be inherited, although it COULD VERY POSSIBLY Make Sense To Do That.this Also Contains Examples of Double Dispatching and The Factory Method, Both of Which Will Be Explained Later.

Exercises 1. Add a class triangle to shapefactory1.java

2. Add a class triangle to shapefactory2.java

3. Add A New Type of GameEnvironment Called Gnomesandfairies to Games.java

4. Modify ShapeFactory2.java so that it uses an Abstract Factory to create different sets of shapes (for example, one particular type of factory object creates "thick shapes," another creates "thin shapes," but each factory object can create all the Shapes: Circles, Squares, Triangles etc.).

When you find that you need to add some new types to a system, the most sensible approach is to create a public interface for these new types with Polymorphism. This allows the code of the rest of the system to be separated from those of the newly added specific types. Adding new types may not change existing code. . . Or at least it looks unnecessary. It seems that if you modify the code with this design method mentioned above, the only thing that needs to be changed is to inherit the new type, but in fact is not exactly the case. You still need to create new types of objects, and where you must specify which constructor is used. Therefore, if the part of the code used to create an object is dispersed throughout the program (you will face this problem when adding a new type), you still need to find all the code related to the type. In this case, the main problem is the creation of the new type (object) rather than the new type of use (this problem can be solved by polymorphism), but the actual effect is the same: adding a new type will always bring problems. The solution is to force a COMMON FACTORY to create an object without allowing code to create an object to be dispersed throughout the system. If all those who need to create objects are created with this factory (Factory), then you need to do it when you add new types. It is possible to modify this factory. Because all object-oriented code will be used to create objects, and in most cases you will extend programs by adding new types, I guess the Factory mode (series) may be the most widely used design mode. Although only Simple Factory Method is a single piece (Singleton), you will find that every specific factory class (these classes belong to more common Factory mode) actually only one instance. Simple factory method (Simple Factory Method)

We take a revolution with the following examples. (Implementing Factory mode) Commonly used method is to declare the Factory as a static method of the base class (STATIC METHOD).

//: Factory: ShapeFact1: ShapeFactory1.java

// a Simple Static Factory Method.

Package factory.shapefact1;

Import java.util. *;

Import junit.framework. *;

Abstract class shape {

Public Abstract void Draw ();

Public abstract void erases;

Public Static Shape Factory (String Type) {

IF (Type.equals ("Circle")) Return New Circle ();

IF (Type.equals ("Square")) Return New Square ();

Throw new runtimeException

"Bad Shape Creation:" TYPE);

}

}

Class Circle Extends Shape {

Circle () {} // package-access constructor

Public void draw () {

System.out.println ("Circle.Draw");

}

Public void erase () {

System.out.println ("Circle.ed");

}

}

Class Square Extends Shape {Square () {} // package-access constructor

Public void draw () {

System.out.println ("Square.Draw");

}

Public void erase () {

System.out.println ("Square.rase");

}

}

Public class shapefactory1 extends testcase {

String shlist [] = {"circle", "Square",

"Square", "Circle", "Circle", "Square"};

List shapes = new arraylist ();

Public void test () {

Iterator it = arrays.aslist (shlist) .Itemrator ();

While (it.hasnext ())

Shapes.add (Shape.Factory ((String) IT.NEXT ());

IT = shapes.iterator ();

While (it.hasnext ()) {

Shape s = (shape) it.next ();

s.draw ();

S.RASE ();

}

}

Public static void main (string args []) {

JUnit.textui.testrunner.run (ShapeFactory1.class);

}

} ///: ~

The factory () method needs to pass a parameter to determine the specific type of Shape to create; in the above example (parameter), it can be a string, which can also be any other type. When adding a new Shape type (we assume that the initialization code of the object is from the system, instead of using a hard-coded array), the only code that needs to be changed is Factory. ()method. In order to cause the code to create an object, only in the factory () method, the constructor of a particular type of Shape class is declared as package permission, so that only the factory () method can call these constructors, and is located (package) The part of the code outside does not have enough permissions (call these constructor).

In the Polymorphic Factories, static factory () methods make all the operations of all creating objects are set in one place, which is the only place where you need to modify the code. This is of course a good solution, which encapsulates the process of creating an object. However, "design mode" emphasizes that using Factory Method is to make different types of factories can be derived from basic types (Subclass). However, the book did not give a specific example, just repeated the example used to explain Abstract Factory (you will see an example of an Abstract Factory in this book). The following example, we modified ShapeFactory1.java to make the factory method a virtual function of a separate class. Please note that a specific type of Shape class is dynamically loaded as needed. //: Factory: Shapefact2: ShapeFactory2.java

// Polymorphic Factory Methods.

Package factory.shapefact2;

Import java.util. *;

Import junit.framework. *;

Interface shape {

Void Draw ();

void erases;

}

Abstract class shapefactory {

Protected abstract shape create ();

Private static map factories = new hashmap ();

Public static void

AddFactory (String ID, ShapeFactory f) {

Factories.put (ID, f);

}

// a Template Method:

Public Static Final

Shape createShape (String ID) {

IF (! Factories.Containskey (ID)) {

Try {

// load dynamically

Class.Forname ("Factory.ShapeFact2." ID);

} catch (classnotfoundexception e) {

Throw new runtimeException

"Bad Shape Creation:" ID);

}

// See if it is put in:

IF (! Factories.ContainsKey (ID))

Throw new runtimeException

"Bad Shape Creation:" ID);

}

Return

(ShapeFactory) Factories.get (ID)). CREATE ();

}

}

Class Circle Implements Shape {

private circle () {}

Public void draw () {

System.out.println ("Circle.Draw");

}

Public void erase () {

System.out.println ("Circle.ed");

}

Private static class factory

Extends shapefactory {

Protected Shape Create () {

Return new circle ();

}

}

STATIC {

ShapeFactory.AddFactory (

"Circle", new factory ());

}

Class Square Implements Shape {

Private square () {}

Public void draw () {

System.out.println ("Square.Draw");

}

Public void erase () {

System.out.println ("Square.rase");

}

Private static class factory

Extends shapefactory {

Protected Shape Create () {

Return new Square ();

}

}

STATIC {

ShapeFactory.AddFactory (

"Square", new factory ());

}

}

Public Class ShapeFactory2 Extends Testcase {

String shlist [] = {"circle", "Square",

"Square", "Circle", "Circle", "Square"};

List shapes = new arraylist ();

Public void test () {

// this Just Makes Sure IT Will Complete

// WITHOUT THROWING AN Exception.

Iterator it = arrays.aslist (shlist) .Itemrator ();

While (it.hasnext ())

Shapes.add

ShapeFactory.createshape (String) IT.Next ()));

IT = shapes.iterator ();

While (it.hasnext ()) {

Shape s = (shape) it.next ();

s.draw ();

S.RASE ();

}

}

Public static void main (string args []) {

JUnit.textui.teStrunner.run (ShapeFactory2.class);

}

} ///: ~

Now the factory method appears in its own class ShapeFactory, the name is changed to the create () method. It is a protected method, that is, it cannot be called directly, but can be overloaded. Subclas for the Shape class must create subclasses of the corresponding ShapeFactory and create its own instance by overloading the create () function. In fact, the creation of a series of Shape objects is done by calling ShapeFactory. CreateShape (). CreateShape () is a static method that found the corresponding factory object (Factory Obejct) based on the incoming markup. Then, find the Factory object is used to create a Shape object, but you can imagine a more tricky question: (corresponding to some Shape type) factory object is used to create objects in more complicated manner . However, in most cases, you don't do not need to use a complicated polymorphic factory method, add a static method in the base class (like ShapeFactory1.java) is enough to solve the problem. Note that the initialization of ShapeFactory must be completed by loading the MAP data (the element of the MAP is a Factory object), and these initialization code is also in the static initialization statement of the Shape implementation class. In this way, every new type you must have to inherit the original type (refer to Shape?), Create a Factory, then add a static initialization statement to load the MAP object. These additional complexities have again suggest us: If you don't need to create a separate Factory object, it is best to use a static factory method. Abstract Factory Abstract Factory The pattern looks like the Factory object we see in front, but it has multiple rather than a Factory method. Each Factory method creates a different type of object. Basic ideas are: In the place where the factory object is created, you will determine how the objects created using the factory object. The example given in "Design Mode" implements portability between different user graphics interfaces (Guis): You create a corresponding Factory object based on your own GUI, after this, when you need to use When you go to the menu, buttons, scroll these things, it automatically creates the appropriate object based on the GUI you use. In this way, you can separate the code to switch between different GUIs to make it concentrated in one place.

As another example, assume you create a universal gaming environment and you still want to support different types of games. The following example gives a possible implementation with an abstract factory.

//: Factory: Games.java

// an Example of the Abstract Factory Pattern.

Package factory;

Import junit.framework. *;

Interface Obstacle {

Void Action ();

}

Interface Player {

Void InteractWith (OBSTACLE O);

}

Class Kitty Implements Player {

Public void interactwith (obstacle ob) {system.out.print ("Kitty Has Encountered A");

ob. activity ();

}

}

Class Kungfuguy Implements Player {

Public void interactwith (Obstacle ob) {

System.out.print ("Kungfuguy Now Battles A);

ob. activity ();

}

}

Class Puzzle IMPLEments OBSTACLE {

Public void action () {

System.out.println ("puzzle");

}

}

Class Nastyweapon Implements Obstacle {

Public void action () {

System.out.println ("NastyWeapon");

}

}

// The Abstract Factory:

Interface GameElementFactory {

Player makeplayer ();

Obstacle makeobstacle ();

}

// Concrete Factories:

Class Kittiesandpuzzles

Implements GameElementFactory {

Public Player makeplayer () {

Return new kitty ();

}

Public Obstacle makeobstacle () {

Return New Puzzle ();

}

}

Class KillandDismember

Implements GameElementFactory {

Public Player makeplayer () {

Return new kungfuguy ();

}

Public Obstacle makeobstacle () {

Return new NastyWeapon ();

}

}

Class GameEnvironment {

Private gameElementFactory Gef;

Private Player P;

Private Obstacle OB;

Public GameEnvironment

GameElementFactory Factory) {

GEF = Factory;

P = factory.makeplayer ();

Ob = factory.makeobstacle ();

}

Public void play () {p.interactwith (ob);

}

Public Class Games Extends Testcase {

GameElementFactory

KP = new kittiesandpuzzles (),

Kd = new killanddismember ();

GameEnvironment

G1 = New GameEnvironment (KP),

G2 = New GameEnvironment (kd);

// Thase Just Ensure No Exceptions Are Thrown:

Public void test1 () {g1.play ();

Public void test2 () {g2.play ();

Public static void main (string args []) {

Junit.textui.teStrunner.Run (Games.class);

}

} ///: ~

转载请注明原文地址:https://www.9cbs.com/read-108225.html

New Post(0)