- Modular Programming with PHP 7
- Branko Ajzele
- 547字
- 2021-07-14 10:06:00
Open/closed principle
The open/closed principle states that a class should be open for extension but closed for modification, as per the definition found on Wikipedia:
"software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"
The open for extension part means that we should design our classes so that new functionality can be added if needed. The closed for modification part means that this new functionality should fit in without modifying the original class. The class should only be modified in case of a bug fix, not for adding new functionality.
The following is an example of a class that violates the open/closed principle:
class CsvExporter { public function export($data) { // Implementation... } } class XmlExporter { public function export($data) { // Implementation... } } class GenericExporter { public function exportToFormat($data, $format) { if ('csv' === $format) { $exporter = new CsvExporter(); } elseif ('xml' === $format) { $exporter = new XmlExporter(); } else { throw new \Exception('Unknown export format!'); } return $exporter->export($data); } }
Here we have two concrete classes, CsvExporter
and XmlExporter
, each with a single responsibility. Then we have a GenericExporter
with its exportToFormat
method that actually triggers the export
function on a proper instance type. The problem here is that we cannot add a new type of exporter to the mix without modifying the GenericExporter
class. To put it in other words, GenericExporter
is not open for extension and closed for modification.
The following is an example of refactored implementation, which complies with OCP:
interface ExporterFactoryInterface { public function buildForFormat($format); } interface ExporterInterface { public function export($data); } class CsvExporter implements ExporterInterface { public function export($data) { // Implementation... } } class XmlExporter implements ExporterInterface { public function export($data) { // Implementation... } } class ExporterFactory implements ExporterFactoryInterface { private $factories = array(); public function addExporterFactory($format, callable $factory) { $this->factories[$format] = $factory; } public function buildForFormat($format) { $factory = $this->factories[$format]; $exporter = $factory(); // the factory is a callable return $exporter; } } class GenericExporter { private $exporterFactory; public function __construct(ExporterFactoryInterface $exporterFactory) { $this->exporterFactory = $exporterFactory; } public function exportToFormat($data, $format) { $exporter = $this->exporterFactory->buildForFormat($format); return $exporter->export($data); } } // Client $exporterFactory = new ExporterFactory(); $exporterFactory->addExporterFactory( 'xml', function () { return new XmlExporter(); } ); $exporterFactory->addExporterFactory( 'csv', function () { return new CsvExporter(); } ); $data = array(/* ... some export data ... */); $genericExporter = new GenericExporter($exporterFactory); $csvEncodedData = $genericExporter->exportToFormat($data, 'csv');
Here we added two interfaces, ExporterFactoryInterface
and ExporterInterface
. We then modified the CsvExporter
and XmlExporter
to implement that interface. The ExporterFactory
was added, implementing the ExporterFactoryInterface
. Its main role is defined by the buildForFormat
method, which returns the exporter as a callback function. Finally, the GenericExporter
was rewritten to accept the ExporterFactoryInterface
via its constructor, and its exportToFormat
method now builds the exporter by use of an exporter factory and calls the execute
method on it.
The client itself has taken a more robust role now, by first instantiating the ExporterFactory
and adding two exporters to it, which it then passed onto GenericExporter
. Adding a new export format to GenericExporter
now, no longer requires modifying it, therefore making it open for extension and closed for modification. Again, this is by no means a universal formula, rather a concept of possible approach towards satisfying the OCP.
- Web程序設計及應用
- Node.js+Webpack開發實戰
- 從零開始:數字圖像處理的編程基礎與應用
- Objective-C應用開發全程實錄
- SQL學習指南(第3版)
- JMeter 性能測試實戰(第2版)
- Scratch 3.0少兒編程與邏輯思維訓練
- C語言程序設計實踐教程
- Amazon S3 Cookbook
- Magento 1.8 Development Cookbook
- Java應用開發技術實例教程
- Building Android UIs with Custom Views
- IBM Cognos Business Intelligence 10.1 Dashboarding cookbook
- 從“1”開始3D編程
- Scratch超人漫游記:創意程序設計:STEAM創新教育指南