In a dense approach to the use of patterns in the design and came across a contradiction between the factory pattern and the 2 SOLID law of principles, which says about openness / closedness (Open-closed): "software entities must be open for expansion, but closed for modification".

Suppose we already have a code (listed below) based on a pattern (template) of the Factory in which data is stored and sent in Json format. Suppose we still have to give the data in a different format. We are based on the interface make the class CsvEncode. But how to deal with the Notepad factory - add the public function method getCsvEncode () there

Is adding a method to a class a violation of the second law of the SOLID principles?

Or in this case, the programmer should review the code and rewrite it under the abstract factory? I am just starting to use design patterns, and in order to apply them appropriately only where needed, i.e. throwing the responsibility of executing code on them, being able to create independent libraries, I need to understand. Maybe I misunderstood the factory pattern in the context of the SOLID principles? I saw that in the factory method they sometimes put the switch case. And depending on the parameter they return an instance of the corresponding class - this is where I found a contradiction, if you need to create another instance of the class - you will have to go into the code and hardcode

<?php //я так понимаю что шаблон фабрика это как правило связь один к одному обоих классов проиходящих от заданных интерфейсов //итак задача - у нас есть данные в виде массива //необходимо получить из этого массива заголовок,подвал, контент //массив отдать в формате json и csv /* *это interface Factory создающий другие классы * */ interface Factory { /* * *@return string| NULL возвращает заголовок */ public function getHeader(); /* * *@return string| NULL возвращает содержимое */ public function getContent(); /* * *@return string| NULL возвращает подвал|нижний колонтитул */ public function getFooter(); /* * *@return Encode возвращает объект для управления данными в формате Json */ public function getJsonEncode(); } /* *это interfaceс Encode для перекодировки * */ interface Encode { public function getEncode(); } /* *класс для перкодировки данных исходящи от класса,поддерживающего интерфейс Factory * *@property Factory $data */ class JsonEncode implements Encode { protected $data; public function __construct(Factory $obj) { $this->data=$obj; } /* *преобразует данные в json формат * *@return string */ public function getEncode() { return json_encode( ['head'=>$this->data->getHeader(),'content'=>$this->data->getContent(),'footer'=>$this->data->getFooter()] ); } } class CsvEncode implements Encode { protected $data; public function __construct(Factory $obj) { $this->data=$obj; } /* *преобразует данные в csv формат * *@return string */ public function getEncode() { return $this->data->getHeader(). PHP_EOL .$this->data->getContent(). PHP_EOL .$this->data->getFooter(); } } /* *класс для перкодировки данных исходящи от класса,поддерживающего интерфейс Factory * *@property string $head *@property string $content *@property string $footer */ class Notepad implements Factory{ protected $head; protected $content; protected $footer; /* * *@param array $arr */ public function __construct($arr){ $this->head=$arr['head']; $this->content=$arr['content']; $this->footer=$arr['footer']; } /* * *@return string| NULL возвращает заголовок */ public function getHeader() { return $this->head; } /* * *@return string| NULL возвращает содержимое */ public function getContent() { return $this->content; } /* * *@return string| NULL возвращает подвал|нижний колонтитул */ public function getFooter() { return $this->footer; } /* * *@return Encode возвращает объект для управления данными в формате Json */ public function getJsonEncode() { return new JsonEncode($this); } } $arr=['head'=>'заголовок','content'=>'содержимое','footer'=>'колонтитул']; $a=new Notepad($arr); echo $a->getJsonEncode()->getEncode();//выводим массив в формате json 

I'm still learning how to use phpDoc, for simplicity I have not used the example of checking for values, catching errors (try {} catch () {}) and automatic connection of classes :) thanks for reading

  • I understand that Notepad is your processor i. The application itself, if so, then another question: why does the processor, instead of implementing its own logic, deal with the implementation of factory logic? And if my memory serves me, among the templates of gang 4 there are only 2 factories, this is the “factory method” and the “abstract factory”. - fens
  • The factory method makes only one type of object, and the abstract factory makes a family of objects. - fens
  • By sabzh, you all have to add a decision point exactly the question of where the switch will be, no, of course, you can dodge and add to your simple factory the logic of finding the desired class by dynamically generating the name of the desired class something like $ type = 'csv'; $ className = $ type. 'Encoder'; // the rest is logic. And in case the required class was not found, return a Null object. - fens
  • Just now I read the code ... The fact that you portrayed it as if not even a factory is more like some kind of builder. And it’s still not clear why you need such a solution as far as I understand the Notepad code you have is a Request-object and it shouldn’t know anything at all, but your Encoders, and eclairs should not know anything, and some factory ... They are made by the factory and receive the Request-object and its data from the factory as input (data from the factory means that the factory initit the encoder with the data it received from the client, and not herself.). - fens

1 answer 1

To begin with, I will make a small lyrical digression and talk a little about terminology.


Generally speaking, you do not correctly interpret the "Factory" pattern. A factory is a generating pattern in which the creation of objects is assigned to a special object. Here is the simplest implementation of this pattern:

 interface FactoryInterface { public function createInstance(); } class FooFactory implements FactoryInterface { public function createInstance() { return new Foo(); } } class GooFactory implements FactoryInterface { public function createInstance() { return new Goo(); } } class Client { private $factory; public function __construct(FactoryInterface $factory) { $this->factory = factory; } public function doSomething() { return $this->factory->createInstace()->doSomething(); } } 

There are several modifications of this pattern:

The main task of the "Factory" pattern (as well as its modifications) is to encapsulate the logic of creating objects in one place .


In your case, I would not talk about the factory (and other patterns), but instead I would simply break the objects by type of responsibility, according to the Single Responsibility Principle. For example, like this:

 class Document { public function getHeader() {/* ... */} public function getBody() {/* ... */} public function getFooter() {/* ... */} } interface Formatter { public function format(Document $doc); } class JsonFormatter { public function format(Document $doc) { return json_encode([ 'header' => $doc->getHeader(), 'body' => $doc->getBody(), 'footer' => $doc->getFooter(), ]); } } class CsvFormatter { public function format(Document $doc) { return sprintf( "header;body;footer\n%s;%s;%s", $doc->getHeader(), $doc->getBody(), $doc->getFooter() ); } } $doc = new Document(); $formatter = new JsonFormatter(); echo $formatter->format($doc); 

It seems to me that this will be more than enough to solve the main problem. In addition, such an implementation follows the Open-Closed Principal you mentioned: to create another formatting class, you do not need to change already existing classes.

(As an exercise, you can independently check if this code should follow the remaining principles of SOLID.)

When designing the main thing to remember the following: patterns for patterns - the way to a big headache!

  • The problem is that many templates seem to be the holy grail of programming. And as long as their bumps are not filled, they will not understand where and what to use, and where you can and even need to do without templates. - fens
  • one
    @fens, and for this reason, those who have already stuffed cones should open the eyes of young people;) - Dmitriy Simushev
  • fens is still right - having a switch case in a factory is detrimental to the expansion of the program. I also understand why the proposed code as an example looks more like a builder template - Mcile
  • Dmitriy Simushev is amazingly beautiful code that helped me understand the phrase fens - The problem is that many templates seem like the holy grail of programming. I proceeded from the principle that I need to write at least 1 time an example of each template in order to understand how it works, and then go to the framework Yii2 and see the templates embedded in it. And you are right, I increasingly understand that every programmer must fill his bumps, write his own CMS, reinvent the bicycle 200 times, and don’t need to add patterns to reach your level of understanding. - Mcile