From Wikipedia, the free encyclopedia
Фабрички метод је објектно-оријентисани пројектни узорак. Као и остали пројектни узорци креирања, бави се проблемима креирања објеката без навођења конкретне класе објекта који се креира. Фабрички метод решава овај проблем тако што дефинише посебну методу за креирање објеката, коју онда поткласе преклапају да одреде тачан подтип објекта (тј. изведену класу) који ће бити креиран. Општије, фабрички метод се често користи да означи било коју методу чија је основна сврха креирање објеката.
Суштина фабричког метода је да „дефинише интерфејс за креирање објеката, али да остави поткласама да одлуче чије објекте креирају. Фабрички метод допушта класи да делегира стварање објекта поткласи.“[1]
Фабрички метод се често користи у радним оквирима (енгл. ) где кôд радног оквира треба да креира објекте којима је познат само надтип (који дефинише радни оквир), али не и конкретни подтип објекта који дефинише клијентски кôд који користи овај радни оквир.
Паралелне класе хијерархија често захтевају да објекти једне хијерархије креирају објеке из друге хијерархије.
Фабрички методи се често користе у развоју да би се класе лакше тестирале. [2] Претпоставимо да постоји класа која креира други објекат класе који се не може аутоматизовано тестирати јединичним тестовима (нпр. комуницира са продукционом базом). Тада се креирање објекта ставља у виртуелну фабричку методу у класу . За тестирање, прави се класа са преклопљеном методом која креира и враћа лажни објекат . Јединични тестови сада користе да тестирају функционалност коју даје класа без опасности и споредних ефеката који би настали да се користи објекат .
Иако је основна мотивација фабричког метода да дозволи поткласама да одлуче тип објекта које креирају, постоје и остале предности коришћења фабричког метода, од којих многе не зависе од наслеђивања класа. Отуда, честе се дефинишу „фабрички методи“ који уопште нису полиморфни и који служе за креирање објеката, а само да би се добиле ове остале предности. Такви методи су обично статички.
Фабрички метод може имати јасно име. Проблем је што у многим објектно-оријентисаним језицима, конструктор мора имати исто име као и класа у којој се налази, а то некад може довести до вишезначности ако постоји више од једног начина да се креира објекат. Фабрички методи немају оваква ограничења и могу имати описна имена. Као пример, када се комплексни бројеви креирају од два реална броја, та два броја могу да представљају или картезијанске или поларне координате, али коришћењем фабричке методе, значење је јасно (следећи примери су написани у програмском језику Јава):
class Complex
{
public static Complex fromCartesian(double real, double imag) {
return new Complex(real, imag);
}
public static Complex fromPolar(double modulus, double angle) {
return new Complex(modulus * cos(angle), modulus * sin(angle));
}
private Complex(double a, double b) {
//...
}
}
Complex c = Complex.fromPolar(1, pi); // Isto kao i fromCartesian(-1, 0)
Када се фабрички методи користе да се овако разреше вишезначности, обично се конструктори стављају да буду приватни да би се на тај начин клијенти приморали да користе фабричке методе.
Фабрички методи енкапсулирају креирање објеката. Ово може бити корисно када је процес креирања објекта јако сложен (нпр. зависи од подешавања у конфигурационим датотекама или од корисничког уноса).
Размотрићемо пример програма који чита слике и од њих прави мање сличице. Програм подржава различите формате слика, представљене са по једном класом за читање сваког од формата:
public interface ImageReader {
public DecodedImage getDecodedImage();
}
public class GifReader implements ImageReader {
public DecodedImage getDecodedImage() {
return decodedImage;
}
}
public class JpegReader implements ImageReader {
// ....
}
Сваки пут када програм прочита слику, мора да, на основу неких информација из датотеке, креира читача за одговарајући формат. Ова логика се може енкапсулирати у фабрички метод:
public class ImageReaderFactory {
public static ImageReader getImageReader(InputStream is) {
int imageType = determineImageType(is);
switch(imageType) {
case ImageReaderFactory.GIF:
return new GifReader(is);
case ImageReaderFactory.JPEG:
return new JpegReader(is);
// etc.
}
}
}
Исечак кôда из претходног примера користи наредбу да споји формат слике са одговорајућом фабриком објеката. Исто тако се могао користити и неки вид мапирања, при чему би се наредба заменила са асоцијативним низом чији су индекси формати слике, а чланови фабрике објеката.
Постоје три ограничења везана за коришћење фабричког метода. Први је везан за рефакторисање постојећег кôда, а друга два за наслеђивање.
Complex c = new Complex(-1, 0);
Сва три проблема се могу избећи када би се програмски језици тако изменили да фабрике постану првокласни чланови језика. [3]
abstract class Pizza {
public abstract int getPrice(); // uzima cenu
}
class HamAndMushroomPizza extends Pizza {
public int getPrice() {
return 850;
}
}
class DeluxePizza extends Pizza {
public int getPrice() {
return 1050;
}
}
class HawaiianPizza extends Pizza {
public int getPrice() {
return 1150;
}
}
class PizzaFactory {
public enum PizzaType {
HamMushroom,
Deluxe,
Hawaiian
}
public static Pizza createPizza(PizzaType pizzaType) {
switch (pizzaType) {
case HamMushroom:
return new HamAndMushroomPizza();
case Deluxe:
return new DeluxePizza();
case Hawaiian:
return new HawaiianPizza();
}
throw new IllegalArgumentException("Tip pice " + pizzaType + " nije prepoznat.");
}
}
class PizzaLover {
/*
* Pravi sve dostupne pice i ispisuje njihove cene
*/
public static void main (String args[]) {
for (PizzaFactory.PizzaType pizzaType : PizzaFactory.PizzaType.values()) {
System.out.println("Cena pice " + pizzaType + " je " + PizzaFactory.createPizza(pizzaType).getPrice());
}
}
}
// Izlaz:
// Cena pice HamMushroom je 850
// Cena pice Deluxe je 1050
// Cena pice Hawaiian je 1150
public interface IPizza
{
decimal Price { get; }
}
public class HamAndMushroomPizza : IPizza
{
decimal IPizza.Price
{
get
{
return 8.5m;
}
}
}
public class DeluxePizza : IPizza
{
decimal IPizza.Price
{
get
{
return 10.5m;
}
}
}
public class HawaiianPizza : IPizza
{
decimal IPizza.Price
{
get
{
return 11.5m;
}
}
}
public class PizzaFactory
{
public enum PizzaType
{
HamMushroom,
Deluxe,
Hawaiian
}
public static IPizza CreatePizza(PizzaType pizzaType)
{
IPizza ret = null;
switch (pizzaType)
{
case PizzaType.HamMushroom:
ret = new HamAndMushroomPizza();
break;
case PizzaType.Deluxe:
ret = new DeluxePizza();
break;
case PizzaType.Hawaiian:
ret = new HawaiianPizza();
break;
default:
throw new ArgumentException("Tip pice " + pizzaType + " nije prepoznat.");
}
return ret;
}
}
public class PizzaLover
{
public static void Main(string[] args)
{
Dictionary<PizzaFactory.PizzaType, IPizza> pizzas = new Dictionary<PizzaFactory.PizzaType, IPizza>();
foreach (PizzaFactory.PizzaType pizzaType in Enum.GetValues(typeof(PizzaFactory.PizzaType)))
{
pizzas.Add(pizzaType, PizzaFactory.CreatePizza(pizzaType));
}
foreach (PizzaFactory.PizzaType pizzaType in pizzas.Keys)
{
System.Console.WriteLine("Cena pice {0} je {1}", pizzaType, pizzas[pizzaType].Price);
}
}
}
// Izlaz:
// Cena pice HamMushroom je 8.5
// Cena pice Deluxe je 10.5
// Cena pice Hawaiian je 11.5
#
# Pica
#
class Pizza:
def __init__(self):
self.price = None
def get_price(self):
return self.price
class HamAndMushroomPizza(Pizza):
def __init__(self):
self.price = 8.5
class DeluxePizza(Pizza):
def __init__(self):
self.price = 10.5
class HawaiianPizza(Pizza):
def __init__(self):
self.price = 11.5
#
# Fabrika pica
#
class PizzaFactory:
@staticmethod
def create_pizza(pizza_type):
if pizza_type == 'HamMushroom':
return HamAndMushroomPizza()
elif pizza_type == 'Deluxe':
return DeluxePizza()
elif pizza_type == 'Hawaiian':
return HawaiianPizza()
if __name__ == '__main__':
for pizza_type in ('HamMushroom', 'Deluxe', 'Hawaiian'):
print 'Cena pice {0} je {1}'.format(pizza_type, PizzaFactory.create_pizza(pizza_type).get_price())
<?php
abstract class Pizza
{
protected $_price;
public function getPrice()
{
return $this->_price;
}
}
class HamAndMushroomPizza extends Pizza
{
protected $_price = 8.5;
}
class DeluxePizza extends Pizza
{
protected $_price = 10.5;
}
class HawaiianPizza extends Pizza
{
protected $_price = 11.5;
}
class PizzaFactory
{
public static function createPizza($type)
{
$baseClass = 'Pizza';
$targetClass = ucfirst($type).$baseClass;
if (class_exists($targetClass) && is_subclass_of($targetClass, $baseClass))
return new $targetClass;
else
throw new Exception("Tip pice nije prepoznat.");
}
}
$pizzas = array('HamAndMushroom','Deluxe','Hawaiian');
foreach($pizzas as $p) {
printf(
"Cena pice %s je %01.2f".PHP_EOL ,
$p ,
PizzaFactory::createPizza($p)->getPrice()
);
}
// Izlaz:
// Cena pice HamMushroom je 8.50
// Cena pice Deluxe je 10.50
// Cena pice Hawaiian je 11.50
?>
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.