- natalia.kaczynska.programista@gmail.com
Factory (Fabryka)
Załóżmy, że chcemy napisać prosty program, który wypisuje jaką standardowo mamy pogodę w zależności od pory roku. W najprostszym podejściu tworzymy obiekty pór roku za pomocą operatora new bezpośrednio w kodzie. Takie rozwiązanie działa, ale ma istotne wady:
klasa korzystająca z obiektów musi znać szczegóły ich tworzenia,
jeśli chcemy zmienić logikę inicjalizacji obiektów, musimy modyfikować wiele fragmentów kodu,
kod staje się trudniejszy do utrzymania i mniej elastyczny.
Poniżej przedstawiam przykład kodu bez zastosowania wzorca projektowego Factory:
Napierw tworzymy interfejs Season:
interface Season {
void showWeather();
}
Następnie tworzymy klasy odpowiadające porom roku i implementujemy interfejs. Np. klasa dla jesieni:
public class Autumn implements Season{
public void showWeather(){
System.out.println("Autumn - it's rainy and cloudy");
}
}
Podobnie tworzymy klasy dla pozostałych pór roku. Na koniec, wywołujemy obiekty tych klas z użyciem wyżej wspomnianego operator new:
public class Demo {
public static void main(String[] args){
Season spring = new Spring();
spring.showWeather();
Season winter = new Winter();
winter.showWeather();
Season summer = new Summer();
summer.showWeather();
Season autumn = new Autumn();
autumn.showWeather();
}
}
Wzorzec Fabryka można podzielić na dwa rodzaje.
Jednym z nich jest metoda fabrykująca. Poniżej pokazuję jak ona działa, modyfikując przykład z porami roku:
Najpierw tworzymy klasę abstrakcyjną Factory:
package season;
public abstract class Factory {
abstract public Season getSeason(SeasonType type);
}
Następnie enum SeasonType:
package season;
public enum SeasonType {
AUTUMN, SUMMER, WINTER, SPRING;
}
Następnie implementujemy fabrykę konkretną:
package season;
public class WeatherFactory extends Factory{
@Override
public Season getSeason(SeasonType type) {
switch(type){
case SPRING:
return new Spring();
case AUTUMN:
return new Autumn();
case SUMMER:
return new Summer();
case WINTER:
return new Winter();
default:
throw new UnsupportedOperationException("No such type");
}
}
}
W klasie Demo:
import season.*;
public class Demo {
public static void main(String[] args){
Factory factory = new WeatherFactory();
Season spring = factory.getSeason(SeasonType.SPRING);
spring.showWeather();
Season winter = factory.getSeason(SeasonType.WINTER);
winter.showWeather();
Season summer = factory.getSeason(SeasonType.SUMMER);
summer.showWeather();
Season autumn = factory.getSeason(SeasonType.AUTUMN);
autumn.showWeather();
}
}
Drugi rodzaj fabryki, to fabryka abstrakcyjna. Tutaj mamy interfejs fabryki, który tworzy rodziny powiązanych obietków (w naszym przykładzie związane z porami roku), zamiast pojedynczego obiektu. Załóżmy, że dana pora roku może mieć cechy charakterystyczne, jak krajobraz, czy typ ubrań. Poniżej zmodyfikuję kod tak, aby uwzględnić ten wzorzec, dla uproszczenia i przejrzystości odwołam się tylko do jesieni i lata, ale oczywiście podobnie można zrobić dla pozostałych pór.
Najpierw tworzę interfejs dla krajobrazu:
package season;
public interface Landscape {
public String description();
}
Podobnie robię dla typu ubrań:
package season;
public interface ClothesType {
public String description();
}
Teraz dla jesieni, tworzę klasę, która implementuje klasę krajobrazu:
package season;
class AutumnLandscape implements Landscape {
public String description() {
return "Falling leaves, fog.";
}
}
Teraz klasa implementująca typ ubrań dla jesieni:
package season;
public class AutumnClothesType implements ClothesType{
public String description() {
return "Wellies and a raincoat.";
}
}
Dla pory roku lato robię analogicznie, oczywiście zmieniając jej opisy.
Następnie tworzę fabrykę abstrakcyjną:
package season;
public interface FactorySeasons {
public Landscape createLandscape();
public ClothesType createClothes();
}
Teraz należy utworzyć konkretne fabryki.
package season;
public class AutumnFactory implements FactorySeasons{
public Landscape createLandscape() {
return new AutumnLandscape();
}
public ClothesType createClothes() {
return new AutumnClothesType();
}
}
package season;
public class SummerFactory implements FactorySeasons {
public Landscape createLandscape() {
return new SummerLandscape();
}
public ClothesType createClothes() {
return new SummerClothesType();
}
}
Na koniec implementuję klasę Demo.
import season.*;
public class Demo {
public static void showSeasonDescription(FactorySeasons factory) {
Landscape landscape = factory.createLandscape();
ClothesType clothes = factory.createClothes();
System.out.println("Landscape: " + landscape.description());
System.out.println("Clothes type: " + clothes.description());
}
public static void main(String[] args) {
System.out.println("Summer: ");
showSeasonDescription(new SummerFactory());
System.out.println();
System.out.println("Autumn: ");
showSeasonDescription(new AutumnFactory());
}
}