Java factory class parameter

Java factory class parameter

Я понимаю что в 1м примере с кофе нужно разделить создание экземпляров от приготовления кофе. Мне не понятно, а не проще ли просто было создать метод createCoffee в этом же классе CoffeeShop, а не городить дополнительный класс? Я не настаиваю что я прав, но хочу понять что бы разобраться. public class CoffeeShop < public Coffee createCoffee (CoffeeType type) < Coffee coffee = null; switch (type) < case AMERICANO: coffee = new Americano(); break; case ESPRESSO: coffee = new Espresso(); break; case CAPPUCCINO: coffee = new Cappuccino(); break; case CAFFE_LATTE: coffee = new CaffeLatte(); break; >return coffee; > public Coffee orderCoffee(CoffeeType type) < /*Coffee coffee = null; switch (type) < case AMERICANO: coffee = new Americano(); break; case ESPRESSO: coffee = new Espresso(); break; case CAPPUCCINO: coffee = new Cappuccino(); break; case CAFFE_LATTE: coffee = new CaffeLatte(); break; >*/ Coffee coffee = createCoffee(type); coffee.grindCoffee(); coffee.makeCoffee(); coffee.pourIntoCup(); System.out.println(«Вот ваш кофе! Спасибо, приходите еще!»); return coffee; > >

У данного примера с кофе, мы все также не решаем проблему маштабирования. Если у нас появляется новый вид кофе, то нам нужно вносить изменения в 2 класса SimpleCoffeeFactory и CoffeeType. «Если ассортимент изменится, нам не придется править код везде, где будет использоваться создание кофе. Достаточно будет изменить код только в одном месте.» — остается проблемой.

А как это дело запустить то? В мэйне мы же не можем создать экземпляр кофешоп. Статик сделать тоже не получается. Как вывести то в консоль что кофе готово?

 Хотя все могло быть еще проще, если сделать метод createCoffee статичным. Но тогда мы потеряли бы две возможности: 1. Наследоваться от SimpleCoffeeFactory и переопределять метод createCoffee . 

Почему не будет возможности наследоваться и тем более переопределять метод, если createCoffe будет с модификатором static?

Читайте также:  JavaScript Ajax GET Demo

Использовать switch тоже не совсем хорошая практика. Это претензия не к автору, почему то во всех примерах используют case. Но это как-то не красиво, да и в реальных проектах я такого не встречал

Источник

Factory Method Pattern

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

In this post we will have a look at the Simple Factory that will lead us to the Factory Method Design Pattern. The extension of the Factory Method will lead us to the Abstract Factory Pattern which we will see in the later posts.

Introduction

The TelevisionManufacturer class is responsible for creating Televisions. There are different kinds of television based on the type like LED, LCD and Plasma. Here’s a first attempt at the implementation.

Simple Implementation

package com.javadevcentral.pattern.factory.simple; import com.javadevcentral.pattern.factory.TelevisionType; import com.javadevcentral.pattern.factory.televisions.LCDTelevision; import com.javadevcentral.pattern.factory.televisions.LEDTelevision; import com.javadevcentral.pattern.factory.televisions.PlasmaTelevision; import com.javadevcentral.pattern.factory.televisions.Television; public class TelevisionManufacturer < public Television manufactureTelevision(TelevisionType televisionType) < Television television; switch (televisionType) < case LED: television = new LEDTelevision(); break; case LCD: television = new LCDTelevision(); break; case PLASMA: television = new PlasmaTelevision(); break; default: throw new RuntimeException("Unknown Television type " + televisionType); >television.checkQuality(); television.box(); return television; > >

package com.javadevcentral.pattern.factory; public enum TelevisionType

Let Television be a simple class with a set of properties and let LEDTelevision, LCDTelevision and PlasmaTelevision inherit the Television class.

After creating a television, the manufacturer does some operations like QA check and finally boxes it. At runtime, it creates a Television object based on the type of television that is needed. There are several problems with this:

  • When we have to add (or remove) a new Television type, this class has to be changed. This clearly violates the Open Closed Principle (OCP) principle which states that classes must be open for extension but closed for modifications.
  • Often, creating a Television object for a given type might be needed in several other places too. Reuse of this logic is not possible with this structure or implementation.
  • Creating concrete classes in this class ties this class with the implementation details.

To solve this, we look at the design principle ‘Encapsulate what varies’. It says – identify the aspects that vary and separate them from what stays the same (We applied this in the Strategy Pattern too).

Simple Factory

The part of the code that creates concrete Television objects is the part that varies. So, we encapsulate it into a new object – the SimpleTelevisionFactory . Whenever we need a new Television (in the TelevisionManufacturer ), we just call the createTelevision method on the TelevisionFactory.

package com.javadevcentral.pattern.factory.simple; import com.javadevcentral.pattern.factory.TelevisionType; import com.javadevcentral.pattern.factory.televisions.LCDTelevision; import com.javadevcentral.pattern.factory.televisions.LEDTelevision; import com.javadevcentral.pattern.factory.televisions.PlasmaTelevision; import com.javadevcentral.pattern.factory.televisions.Television; public class SimpleTelevisionFactory < public Television createTelevision(TelevisionType televisionType) < Television television; switch (televisionType) < case LED: television = new LEDTelevision(); break; case LCD: television = new LCDTelevision(); break; case PLASMA: television = new PlasmaTelevision(); break; default: throw new RuntimeException("Unknown Television type " + televisionType); >return television; > >

In this way, our manufacturer client code is not tied to any concrete Television class (it depends on the Television abstraction). This also enables any other code to reuse the TelevisionFactory to create Television objects. This also makes maintenance easy as there is just one place to change (the TelevisionFactory) when the type of televisions to support changes in the future.

The TelevisionManufacturer code now becomes,

package com.javadevcentral.pattern.factory.simple; import com.javadevcentral.pattern.factory.TelevisionType; import com.javadevcentral.pattern.factory.televisions.Television; public class TelevisionManufacturer < private SimpleTelevisionFactory simpleTelevisionFactory; public TelevisionManufacturer(SimpleTelevisionFactory simpleTelevisionFactory) < this.simpleTelevisionFactory = simpleTelevisionFactory; >public Television manufactureTelevision(TelevisionType televisionType) < Television television = simpleTelevisionFactory.createTelevision(televisionType); television.checkQuality(); television.box(); return television; >>

We pass the TelevisionManufacturer a reference to the SimpleTelevisionFactory which it uses to create Televisions.

What we have seen above is an implementation of the ‘Simple Factory’. It is not a design pattern but it is a programming idiom used often. Do not confuse Simple Factory with the Factory Method.

Structure

Supporting multiple manufacturers

Now, we have a Television Manufacturer class that can manufacture top-notch televisions. It is not coupled to any kind of Television object. Now, we need to support different manufacturers for different brands of TVs say, Samsung, LG, Philips, etc. Each manufacturer builds its own classes of Televisions (Samsung LED, Samsung LCD, Philips LEG, Philips LCD etc.)

Multiple Simple Factories

One option is to extend the idea of Simple Factory by replacing the SimpleTelevisionFactory with different factories for each of the manufacturers. Then we can pass the appropriate reference to a factory to our main TelevisionManufacturer

SamsungTelevisionFactory samsungTelevisionFactory = new SamsungTelevisionFactory(); TelevisionManufacturer televisionManufacturer = new TelevisionManufacturer(samsungTelevisionFactory); //Creates Samsung LED TV televisionManufacturer.manufactureTelevision(TelevisionType.LED); PhilipsTelevisionFactory philipsTelevisionFactory = new PhilipsTelevisionFactory(); TelevisionManufacturer televisionManufacturer = new TelevisionManufacturer(philipsTelevisionFactory); //Creates Philips LCD TV televisionManufacturer.manufactureTelevision(TelevisionType.LCD);

This would work fine. But what if each manufacturer did the QA check or the boxing differently? So, we need a framework that ties the TV manufacturer and the manufacturing together.

Factory Method

We will bring the createTelevision method into the TelevisionManufacturer class but as an abstract method. We will have subclasses (of TelevisionManufacturer) for the different manufacturers (SamsungManufacturer, PhilipsManufacturer etc). They will provide an implementation for the createTelevision and they will fully own the Television creation logic. Since the createTelevision method is now abstract the TelevisionManufacturer class will also be marked abstract.

package com.javadevcentral.pattern.factory.method; import com.javadevcentral.pattern.factory.TelevisionType; import com.javadevcentral.pattern.factory.televisions.Television; public abstract class TelevisionManufacturer < public Television manufactureTelevision(TelevisionType televisionType) < Television television = createTelevision(televisionType); television.checkQuality(); television.box(); return television; >protected abstract Television createTelevision(TelevisionType televisionType); >

From the TelevisionManufacturer standpoint, it just calls the createTelevision abstract method, and it gets back a Television object. It does not know what kind of television is being returned. It is solely decided by the choice of the subclass (manufacturer) we choose to manufacture from.

Let us take a look at the individual manufacturers.

package com.javadevcentral.pattern.factory.method; import com.javadevcentral.pattern.factory.TelevisionType; import com.javadevcentral.pattern.factory.televisions.SamsungLCDTelevision; import com.javadevcentral.pattern.factory.televisions.SamsungLEDTelevision; import com.javadevcentral.pattern.factory.televisions.SamsungPlasmaTelevision; import com.javadevcentral.pattern.factory.televisions.Television; public class SamsungTelevisionManufacturer extends TelevisionManufacturer < @Override protected Television createTelevision(TelevisionType televisionType) < Television television; switch (televisionType) < case LED: television = new SamsungLEDTelevision(); break; case LCD: television = new SamsungLCDTelevision(); break; case PLASMA: television = new SamsungPlasmaTelevision(); break; default: throw new RuntimeException("Unknown Television type " + televisionType); >return television; > >

Similarly a PhilipsTelevisionManufacturer will look the same way except that it will return PhilipsLEDTelevision , PhilipsLCDTelevision and PhilipsPlasmaTelevision .

The abstract method on the TelevisionManufacturer is called the factory method. It takes care of object creation and encapsulates it in the subclass.

For the sake of completeness, a Television class described so far might look like this (The exact details are not important here).

package com.javadevcentral.pattern.factory.televisions; import java.util.List; public class Television < private String model; private int warrantyInYears; private ListspecialFeatures; public void checkQuality() < System.out.println("Checking the Quality of the TV"); >public void box() < System.out.println("Packing the TV in a box"); >>

Structure

Participants

Product (Television)

ConcreteProduct (SamsungLEDDocument)

Creator (TelevisionManufacturer)

  • Declares the factory method, which returns an object of type Product. It calls the factory method to create a Product object.

ConcreteCreator (SamsungTelevisionManufacturer)

Other Forms of Factory Method

Default implementation of the factory method : In some cases, the Creator class can provide a default implementation of the factory method. In such cases, the Creator class can be marked non-abstract.

Non-parameterized factory method : The version of the factory method that we saw involves sending a parameter to the factory method using which the subclasses made some decision. This is called a parameterized factory method. There is also another version that does not have any parameters.

Factory Method – Pros

  • Less coupling: No need to couple the creator class with specific concrete (Product) classes. It does this by moving the concrete Product object creation to the subclasses.
  • SRP and OCP: With above, we also adhere to the Single Responsibility Principle (SRP) and the Open Closed Principle (OCP) .
  • Provides a hook for subclasses: When the factory method provides a default implementation, the factory method effectively becomes a hook. The subclasses can override this method to return a more specific object when the default implementation does not apply. For others, the default implementation would be sufficient.
  • Provides a framework: It allows us to build a framework involving the object creation through subclass. Usually the factory method is called from one of the methods (sometimes marked as final) in the abstract creator. Once the object (product) is created and returned, it does some operations on it. Thus, this other method provides a framework whereas the subclass decide what product object to create.

Factory Method – Cons

  • Need to subclass: We have to create a subclass to implement the abstract factory method just to create a product.

Design principles used

  • Program to an interface, not implementations
  • Encapsulate what varies – Identify the aspects of your application that vary and separate them from what stays the same.

Conclusion

The Factory Method allows us to encapsulate the creation of concrete objects. This leads to less coupling, code reuse, and easier maintenance. The Abstract creator has an (abstract) factory method that acts as a hook for the subclasses to create concrete product objects. The Abstract Creator does know about the concrete products that are created. It is decided by our choice of the subclass to be used. Usually the other methods in the Abstract Creator operate on the product object returned by the factory method.

References and Resources

  1. Head First Design Patterns: A Brain-Friendly Guide by Eric Freeman and Elisabeth Robson.
  2. Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.
  3. Why is the factory method design pattern more useful than having classes and calling them individually?

Share This Post Share this content

Источник

Оцените статью