├── .gitignore ├── LICENSE ├── README.md ├── composer.json └── src ├── AbstractServer ├── RedRose.php ├── Rose.php ├── ShopOwner.php └── YellowRose.php ├── ActiveObject ├── MultipleFileUploader.php └── UploadCommand.php ├── Adapter ├── ProductInterface.php ├── RealWhiteRose.php ├── RoseToProductAdapter.php └── TheOldRoseInterface.php ├── Bridge ├── CreditBuyer.php ├── CreditPayment.php ├── CreditPaymentMethod.php ├── DirectBuyer.php ├── DirectPayment.php ├── DirectPaymentMethod.php ├── MasterCard.php ├── PaymentMethod.php ├── PaymentSource.php └── Visa.php ├── Command ├── PaymentMethod.php ├── PaymentProcessingException.php ├── PaymentProcessor.php ├── PaypalPayment.php ├── User.php └── VisaPayment.php ├── Composite ├── CompositeOrder.php ├── Order.php ├── ProductOrder.php └── ServiceOrder.php ├── Decorator ├── HtmlPaymentDetails.php ├── PaymentDecorator.php ├── PaymentMethod.php ├── PaypalPayment.php └── VisaPayment.php ├── EventDispatcher ├── BlogPublisher.php ├── EventManager.php └── SenderInterface.php ├── Facade ├── ClientData.php ├── ClientFacade.php ├── ClientPersonalData.php ├── PaymentHistory.php ├── Statistics.php └── TopPayments.php ├── Factory ├── Keyboard.php ├── Mouse.php ├── Product.php ├── ProductFactory.php └── ShoppingCart.php ├── Gateway ├── CartGateway.php ├── FileCart.php ├── InMemoryCart.php └── ShoppingHistory.php ├── Mediator ├── Mediator.php ├── Observable.php ├── OrderDelivery.php ├── UserAddress.php ├── UserDetails.php └── UserUpdater.php ├── Monostate └── Monostate.php ├── Null ├── NullProduct.php ├── ProductProvider.php └── Receipt.php ├── Observer ├── DesktopNotifier.php ├── EmailNotifier.php ├── HardDisk.php ├── ProductObserver.php └── ProductSubject.php ├── Proxy ├── Cart.php ├── CartProxy.php └── ShoppingCart.php ├── Repository ├── ProductType.php ├── ProductTypeRepository.php ├── TypeFactory.php └── TypeGateway.php ├── Singleton ├── DiscountProvider.php └── PriceCalculator.php ├── State ├── AtDestination.php ├── Delivery.php ├── DeliveryState.php ├── OnRoute.php └── Processing.php ├── Strategy ├── EuropePricingStrategy.php ├── PriceCaculator.php ├── PriceComputer.php └── USAPricingStrategy.php ├── Template ├── Sell.php ├── SellProducts.php └── SellServices.php └── Visitor ├── HtmlPaymentDetails.php ├── PaymentMethod.php ├── PaymentVisitor.php ├── PaypalPayment.php ├── SimplePaymentDetails.php └── VisaPayment.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | composer.lock 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Dinh Quoc Han 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Agile Design Patterns 2 | 3 | _An accepted solution for a common problem_ 4 | 5 | ## Creational design patterns 6 | 7 | ### Factory Method Pattern 8 | 9 | - **Intent** 10 | - Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method 11 | lets a class defer instantiation to subclasses. 12 | - Defining a "virtual" constructor. 13 | - The new operator considered harmful. 14 | - **Check list** 15 | 1. If you have an inheritance hierarchy that exercises polymorphism, consider adding a polymorphic creation 16 | capability by defining a `static` factory method in the base class. 17 | 2. Design the arguments to the factory method. What qualities or characteristics are necessary and sufficient to 18 | identify the correct derived class to instantiate? 19 | 3. Consider designing an internal "object pool" that will allow objects to be reused instead of created from 20 | scratch. 21 | 4. Consider making all constructors `private` or `protected`. 22 | - **When**: Use a Factory Pattern when you find yourself writing code to gather information necessary to create objects. 23 | - **Why**: Factories help to contain the logic of object creation in a single place. They can also break dependencies to 24 | facilitate loose coupling and dependency injection to allow for better testing. 25 | 26 | ### Singleton Pattern 27 | 28 | - **Intent** 29 | - Ensure a class has only one instance, and provide a global point of access to it. 30 | - Encapsulated "just-in-time initialization" or "initialization on first use". 31 | - **Check list** 32 | 1. Define a private static attribute in the "single instance" class. 33 | 2. Define a public static accessor function in the class. 34 | 3. Do "lazy initialization" (creation on first use) in the accessor function. 35 | 4. Define all constructors to be protected or private. 36 | 5. Clients may only use the accessor function to manipulate the Singleton. 37 | - **When**: You need to achieve singularity and want a cross platform, lazily evaluated solution which also offers the 38 | possibility of creation through derivation. 39 | - **Why**: To offer a single point of access when needed. 40 | - **Example** 41 | 42 | ```php 43 | class Singleton 44 | { 45 | private static $instance; 46 | 47 | private function __construct() 48 | { 49 | // Complicated process 50 | } 51 | 52 | public static function getInstance() 53 | { 54 | if (! (static::$instance instanceof Singleton)) { 55 | static::$instance = new Singleton; 56 | } 57 | 58 | return static::$instance; 59 | } 60 | } 61 | ``` 62 | 63 | ### Monostate Pattern 64 | 65 | - **When**: Transparency, derivabitility, and polymorphism are preferred together with singularity. 66 | - **Why**: To hide from the users/clients the fact that the object offers singularity. 67 | - **Example** 68 | 69 | ```php 70 | class Monostate 71 | { 72 | private static $value; 73 | 74 | public function setValue($value) 75 | { 76 | static::$value = $value; 77 | } 78 | 79 | public function getValue() 80 | { 81 | return static::$value; 82 | } 83 | } 84 | ``` 85 | 86 | ## Structural patterns 87 | 88 | ### Adapter Pattern 89 | 90 | - **Intent** 91 | - Convert the interface of a class into another interface clients expect. Adapter lets classes work together that 92 | couldn't otherwise because of incompatible interfaces. 93 | - Wrap an existing class with a new interface. 94 | - Impedance match an old component to a new system 95 | - **When**: You need to create a connection with a pre-existing and potentially changing module, library, or API. 96 | - **Why**: To allow your business logic to rely only on the public methods the adapter offers, and permit changing the 97 | other side of the adapter easily. 98 | - **Check list** 99 | 1. Identify the players: the component(s) that want to be accommodated (i.e. the client), and the component that 100 | needs to adapt (i.e. the adaptee). 101 | 2. Identify the interface that the client requires. 102 | 3. Design a "wrapper" class that can "impedance match" the adaptee to the client. 103 | 4. The adapter/wrapper class "has a" instance of the adaptee class. 104 | 5. The adapter/wrapper class "maps" the client interface to the adaptee interface. 105 | 6. The client uses (is coupled to) the new interface 106 | 107 | ### Bridge Pattern 108 | 109 | - **When**: The adapter pattern is not enough, and you change classes on both sides of the pipe. 110 | - **Why**: To offer increased flexibility at the cost of significant complexity. 111 | - **Structure**  112 | - **Check List** 113 | 1. Decide if two orthogonal dimensions exist in the domain. These independent concepts could be: 114 | abstraction/platform, or domain/infrastructure, or front-end/back-end, or interface/implementation. 115 | 2. Design the separation of concerns: what does the client want, and what do the platforms provide. 116 | 3. Design a platform-oriented interface that is minimal, necessary, and sufficient. Its goal is to decouple the 117 | abstraction from the platform. 118 | 4. Define a derived class of that interface for each platform. 119 | 5. Create the abstraction base class that "has a" platform object and delegates the platform-oriented functionality 120 | to it. 121 | 6. Define specializations of the abstraction class if desired. 122 | - **Example** 123 | 124 | ```php 125 | abstract class Switcher 126 | { 127 | protected $device; 128 | 129 | public function __construct(Device $device) 130 | { 131 | $this->device = $device; 132 | } 133 | 134 | public function On() 135 | { 136 | return $this->device->On(); 137 | } 138 | 139 | public function Off() 140 | { 141 | return $this->device->Off(); 142 | } 143 | } 144 | 145 | abstract class Device 146 | { 147 | protected $isOn = false; 148 | 149 | public function On() 150 | { 151 | $this->isOn = true; 152 | } 153 | 154 | public function Off() 155 | { 156 | $this->isOn = false; 157 | } 158 | } 159 | 160 | class Tv extends Device 161 | { 162 | private $channel; 163 | 164 | public function On() 165 | { 166 | $this->isOn = true; 167 | $this->channel = 1; 168 | } 169 | 170 | public function Off() 171 | { 172 | $this->isOn = false; 173 | $this->channel = 2; 174 | } 175 | } 176 | 177 | class Lamp extends Device 178 | { 179 | private $lightColor = 'blue'; 180 | 181 | public function On() 182 | { 183 | $this->isOn = true; 184 | $this->lightColor = 'green'; 185 | } 186 | 187 | public function Off() 188 | { 189 | $this->isOn = false; 190 | $this->lightColor = 'blue'; 191 | } 192 | } 193 | ``` 194 | 195 | ### Composit Pattern 196 | 197 | - **Intent** 198 | - Compose objects into tree structures to represent whole-part hierarchies. Composite lets clients treat individual 199 | objects and compositions of objects uniformly. 200 | - Recursive composition 201 | - "Directories contain entries, each of which could be a directory." 202 | 1-to-many "has a" up the "is a" hierarchy 203 | - **When**: You have to apply an action to several similar objects. 204 | - **Why**: To reduce duplication and simplify how similar objects are called. 205 | - **Structure**  206 | - **Check List** 207 | 1. Ensure that your problem is about representing "whole-part" hierarchical relationships. 208 | 2. Consider the heuristic, "Containers that contain containees, each of which could be a container." For example, " 209 | Assemblies that contain components, each of which could be an assembly." Divide your domain concepts into 210 | container classes, and containee classes. 211 | 3. Create a "lowest common denominator" interface that makes your containers and containees interchangeable. It 212 | should specify the behavior that needs to be exercised uniformly across all containee and container objects. 213 | 4. All container and containee classes declare an "is a" relationship to the interface. 214 | 5. All container classes declare a one-to-many "has a" relationship to the interface. 215 | 6. Container classes leverage polymorphism to delegate to their containee objects. 216 | 7. Child management methods [e.g. addChild(), removeChild()] should normally be defined in the Composite class. 217 | Unfortunately, the desire to treat Leaf and Composite objects uniformly may require that these methods be 218 | promoted to the abstract Component class. See the Gang of Four for a discussion of these "safety" versus " 219 | transparency" trade-offs. 220 | - **Example** 221 | 222 | ```php 223 | // Step 3 224 | interface Component 225 | { 226 | public function doThis(); 227 | } 228 | 229 | class Leaf implements Component 230 | { 231 | public function doThis() 232 | { 233 | // Something 234 | } 235 | } 236 | 237 | class Composite implements Component 238 | { 239 | // Step 5: a one-to-many "has a" relationship 240 | private $elements = []; 241 | 242 | // Step 7 243 | public function addElement(Component $element) 244 | { 245 | $this->elements[] = $element; 246 | } 247 | 248 | // Step 4: "is a" relationship 249 | public function doThis() 250 | { 251 | foreach($this->elements as $element) { 252 | $element->doThis(); // Step 6 253 | } 254 | } 255 | } 256 | ``` 257 | 258 | ### Decorator Pattern 259 | 260 | - **Intent** 261 | - Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to 262 | subclassing for extending functionality. 263 | - Client-specified embellishment of a core object by recursively wrapping it. 264 | - Wrapping a gift, putting it in a box, and wrapping the box. 265 | - **Structure**  266 | - **Check list** 267 | 1. Ensure the context is: a single core (or non-optional) component, several optional embellishments or wrappers, 268 | and an interface that is common to all. 269 | 2. Create a "Lowest Common Denominator" interface that makes all classes interchangeable. 270 | 3. Create a second level base class (Decorator) to support the optional wrapper classes. 271 | 4. The Core class and Decorator class inherit from the LCD interface. 272 | 5. The Decorator class declares a composition relationship to the LCD interface, and this data member is initialized 273 | in its constructor. 274 | 6. The Decorator class delegates to the LCD object. 275 | 7. Define a Decorator derived class for each optional embellishment. 276 | 8. Decorator derived classes implement their wrapper functionality - and - delegate to the Decorator base class. 277 | 9. The client configures the type and ordering of Core and Decorator objects. 278 | - **When**: You can't change old classes, but you have to implement new behavior or state. 279 | - **Why**: It offers an unintrusive way of adding new functionality. 280 | - **Example** 281 | 282 | ```php 283 | interface Present 284 | { 285 | public function show(); 286 | } 287 | 288 | class Gift implements Present 289 | { 290 | public function show() 291 | { 292 | return 'A gift from God!'; 293 | } 294 | } 295 | 296 | abstract class Decorator implements Present 297 | { 298 | protected $present; 299 | 300 | public function __construct(Present $present) 301 | { 302 | $this->present = $present; 303 | } 304 | 305 | public function show() 306 | { 307 | return $this->present->show(); 308 | } 309 | } 310 | 311 | class Package extends Decorator 312 | { 313 | public function pack() 314 | { 315 | return "Packed by Package: {$this->show()}"; 316 | } 317 | } 318 | ``` 319 | 320 | ### Facade Pattern 321 | 322 | - **Intent** 323 | - Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that 324 | makes the subsystem easier to use. 325 | - Wrap a complicated subsystem with a simpler interface. 326 | - **When**: To simplify your API or intentionally conceal inner business logic. 327 | - **Why**: You can control the API and the real implementations and logic independently. 328 | - **Check List** 329 | 1. Identify a simpler, unified interface for the subsystem or component. 330 | 2. Design a 'wrapper' class that encapsulates the subsystem. 331 | 3. The facade/wrapper captures the complexity and collaborations of the component, and delegates to the appropriate 332 | methods. 333 | 4. The client uses (is coupled to) the Facade only. 334 | 5. Consider whether additional Facades would add value. 335 | - **Example** 336 | 337 | ```php 338 | class StubsFacade 339 | { 340 | private $container; 341 | 342 | public function __construct(Container $container) 343 | { 344 | $this->container = $container; 345 | } 346 | 347 | public function findStubs($input) 348 | { 349 | // Do job A 350 | // Do job B 351 | // Do job C 352 | $result = 'some values...'; 353 | // Do job N 354 | 355 | return $result; 356 | } 357 | } 358 | ``` 359 | 360 | ### Proxy Pattern 361 | 362 | - **When**: You have to retrieve information from a persistence layer or external source, but don't want your business 363 | logic to know this. 364 | - **Why**: To offer a non-intrusive approach to creating objects behind the scenes. It also opens the possibility to 365 | retrieve these object on the fly, lazily, and from different sources. 366 | - **Check List** 367 | 1. Identify the leverage or "aspect" that is best implemented as a wrapper or surrogate. 368 | 2. Define an interface that will make the proxy and the original component interchangeable. 369 | 3. Consider defining a `Factory` that can encapsulate the decision of whether a proxy or original object is 370 | desirable. 371 | 4. The wrapper class holds a pointer to the real class and implements the interface. 372 | 5. The pointer may be initialized at construction, or on first use. 373 | 6. Each wrapper method contributes its leverage, and delegates to the wrappee object. 374 | - **Example** 375 | ```php 376 | interface Animal 377 | { 378 | public function run(); 379 | } 380 | 381 | class Cat implements Animal 382 | { 383 | public function run() 384 | { 385 | return 'Cat is running'; 386 | } 387 | 388 | public function meow() 389 | { 390 | return 'Cat is meowing'; 391 | } 392 | } 393 | 394 | class CatProxy implements Animal 395 | { 396 | private $cat; 397 | 398 | public function run() 399 | { 400 | if (is_null($this->cat)) 401 | $this->cat = new Cat; 402 | 403 | return $this->cat->run(); 404 | } 405 | 406 | // CatProxy only provides run() method and hide another unwanted to know methods 407 | } 408 | ``` 409 | 410 | ### Abstract Server Pattern 411 | 412 | - **When**: You need to connect objects and maintain flexibility. 413 | - **Why**: Because it is the simplest way to achieve flexibility, while respecting both the dependency inversion 414 | principle and the open close principle. 415 | - **Example** 416 | 417 | ```php 418 | interface Animal 419 | { 420 | public function run(); 421 | } 422 | 423 | class Cat implements Animal 424 | { 425 | public function run() 426 | { 427 | return 'Cat is running'; 428 | } 429 | } 430 | 431 | class Dog implements Animal 432 | { 433 | public function run() 434 | { 435 | return 'Dog is running'; 436 | } 437 | } 438 | 439 | class ZooKeeper 440 | { 441 | private $animal; 442 | 443 | public function __construct(Animal $animal) 444 | { 445 | $this->animal = $animal; 446 | } 447 | 448 | public function letItOut() 449 | { 450 | $this->animal->run(); 451 | } 452 | } 453 | ``` 454 | 455 | ## Behavioral patterns 456 | 457 | ### Command Pattern 458 | 459 | - **Intent** 460 | - Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log 461 | requests, and support undoable operations. 462 | - Promote "invocation of a method on an object" to full object status 463 | - An object-oriented callback 464 | - **Problem**: Need to issue requests to objects without knowing anything about the operation being requested or the 465 | receiver of the request. 466 | - **When**: When you have to perform many operations to prepare objects for use. 467 | - **Check list** 468 | - Define a Command interface with a method signature like execute(). 469 | - Create one or more derived classes that encapsulate some subset of the following: a "receiver" object, the method 470 | to invoke, the arguments to pass. 471 | - Instantiate a Command object for each deferred execution request. 472 | - Pass the Command object from the creator (aka sender) to the invoker (aka receiver). 473 | - The invoker decides when to execute(). 474 | - **Why**: To move complexity from the consuming code to the creating code. 475 | - **Example** 476 | 477 | ```php 478 | interface Command 479 | { 480 | public function execute(); 481 | } 482 | 483 | class MakeDirectoryCommand implements Command 484 | { 485 | private $dirname; 486 | 487 | public function __construct($dirname) 488 | { 489 | $this->dirname = $dirname; 490 | } 491 | 492 | public function execute() 493 | { 494 | return exec("mkdir {$this->dirname}"); 495 | } 496 | } 497 | 498 | class ListCommand implements Command 499 | { 500 | public function execute() 501 | { 502 | return `ls -al`; 503 | } 504 | } 505 | 506 | class CommandCreator 507 | { 508 | /* 509 | * @return Command 510 | */ 511 | public function createCommand($commandName, $args) 512 | { 513 | switch($commandName) { 514 | case 'mkdir': 515 | return new MakeDirectoryCommand($args['dir_name']); 516 | break; 517 | 518 | case 'ls': 519 | return new ListCommand(); 520 | 521 | default: 522 | throw new InvalidArgumentException("Can't create command: $commandName"); 523 | } 524 | } 525 | } 526 | 527 | class Program 528 | { 529 | private $creator; 530 | 531 | public function __construct(CommandCreator $creator) 532 | { 533 | $this->creator = $creator; 534 | } 535 | 536 | public function mkdir() 537 | { 538 | $command = $this->creator->createCommand('mkdir'); 539 | $command->execute(); 540 | } 541 | 542 | public function ls() 543 | { 544 | $command = $this->creator->createCommand('ls'); 545 | $command->execute(); 546 | } 547 | } 548 | ``` 549 | 550 | ### Mediator Pattern 551 | 552 | - **Intent** 553 | - Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping 554 | objects from referring to each other explicitly, and it lets you vary their interaction independently. 555 | - Design an intermediary to decouple many peers. 556 | - Promote the many-to-many relationships between interacting peers to "full object status". 557 | - **When**: The affected objects can not know about the observed objects. 558 | - **Why**: To offer a hidden mechanism of affecting other objects in the system when one object changes. 559 | - **Example** 560 | 561 | ```php 562 | class Mediator 563 | { 564 | private $observered; 565 | 566 | private $afftecter; 567 | 568 | public function __construct(Observable $observered, Affecter $afftecter) 569 | { 570 | $this->observered = $observered; 571 | $this->afftecter = $afftecter; 572 | } 573 | 574 | public function update($stub) 575 | { 576 | $this->affecter->setStub($stub); 577 | } 578 | } 579 | 580 | abstract class Observable 581 | { 582 | protected $observers = [ ]; 583 | 584 | public function register(Mediator $observer) 585 | { 586 | $this->observers[] = $observer; 587 | } 588 | 589 | public function unregister(Mediator $observer) 590 | { 591 | foreach ($this->observers as $index => $o) { 592 | if ($observer === $o) { 593 | unset( $this->observers[$index] ); 594 | } 595 | } 596 | } 597 | 598 | public function notify(Observable $other) 599 | { 600 | foreach ($this->observers as $observer) { 601 | $observer->update($other); 602 | } 603 | } 604 | } 605 | 606 | class Observered extends Observable 607 | { 608 | private $stub; 609 | 610 | public function setStub($stub) 611 | { 612 | $this->stub = $stub; 613 | 614 | $this->notify($stub); 615 | } 616 | } 617 | 618 | interface Affecter 619 | { 620 | public function setStub($stub); 621 | } 622 | 623 | class Affected implements Affecter 624 | { 625 | private $stub; 626 | 627 | public function setStub($stub) 628 | { 629 | $this->stub = $stub; 630 | } 631 | } 632 | ``` 633 | 634 | ### Null Pattern 635 | 636 | - **Intent**: The intent of a Null Object is to encapsulate the absence of an object by providing a substitutable 637 | alternative that offers suitable default do nothing behavior. In short, a design where "nothing will come of nothing" 638 | - **When**: 639 | - You frequently check for null or you have refused bequests. 640 | - An object requires a collaborator. The Null Object pattern does not introduce this collaboration – it makes use of 641 | a collaboration that already exists 642 | - Some collaborator instances should do nothing 643 | - You want to abstract the handling of null away from the client 644 | - **Why**: It can add clarity to your code and forces you to think more about the behavior of your objects. 645 | - **Structure** 646 | - _Client_ — requires a collaborator. 647 | - _AbstractObject_ 648 | - declares the interface for Client's collaborator 649 | - implements default behavior for the interface common to all classes, as appropriate 650 | - _RealObject_ — defines a concrete subclass of `AbstractObject` whose instances provide useful behavior 651 | that `Client` expects 652 | - _NullObject_ 653 | - provides an interface identical to AbstractObject's so that a null object can be substituted for a real object 654 | - implements its interface to do nothing. What exactly it means to do nothing depends on what sort of 655 | behavior `Client` is expecting 656 | - when there is more than one way to do nothing, more than one `NullObject` class may be required 657 | 658 | ### Observer Pattern 659 | 660 | - **Intent**: 661 | - In the Observer pattern a `subject` object will _notify_ an `observer` object if the subject's state _changes_. 662 | - Define a one-to-many dependency between objects so that when one object changes state, all its dependents are 663 | notified and updated automatically. 664 | - Encapsulate the core (or common or engine) components in a Subject abstraction, and the variable (or optional or 665 | user interface) components in an Observer hierarchy. 666 | - The "View" part of Model-View-Controller. 667 | - **Types**: 668 | - **Polling** – Objects accept subscribers. Subscribers observe the object and are notified on specific events. 669 | Subscribers ask the observed objects for more information in order to take an action. 670 | - **Push** – Like the polling method, objects accept subscribers, and subscribers are notified when an event occurs. 671 | But when a notification happens, the observer also receives a hint that the observer can act on. 672 | - **When**: To provide a notification system inside your business logic or to the outside world. 673 | - **Why**: The pattern offers a way to communicate events to any number of different objects. 674 | - **Check list** 675 | 1. Differentiate between the core (or independent) functionality and the optional (or dependent) functionality. 676 | 2. `Model` the independent functionality with a "subject" abstraction. 677 | 3. `Model` the dependent functionality with an "observer" hierarchy. 678 | 4. The `Subject` is coupled only to the `Observer` base class. 679 | 5. The client configures the number and type of Observers. 680 | 6. Observers register themselves with the Subject. 681 | 7. The `Subject` broadcasts events to all registered Observers. 682 | 8. The `Subject` may `push` information at the Observers, or, the Observers may `pull` the information they need 683 | from the Subject. 684 | - **Example** 685 | 686 | ```php 687 | interface Observer 688 | { 689 | public function onUpdate(Subject $subject); 690 | } 691 | 692 | abstract class Observable 693 | { 694 | private $observers = []; 695 | 696 | public function register(Observer $observer) 697 | { 698 | $this->observers[] = $observer; 699 | } 700 | 701 | public function unregister(Observer $observer) 702 | { 703 | foreach($this->observers as $index => $o) { 704 | if ($observer === $o) { 705 | unset($this->observers[$index]); 706 | } 707 | } 708 | } 709 | 710 | public function notify() 711 | { 712 | foreach($this->observers as $observer) { 713 | $observer->onUpdate($this); 714 | } 715 | } 716 | } 717 | 718 | class User extends Observable 719 | { 720 | private $username; 721 | 722 | public function setUsername($username) 723 | { 724 | $this->username = $username; 725 | $this->notify(); 726 | } 727 | } 728 | 729 | class Notifier implements Observer 730 | { 731 | public function onUpdate(Subject $subject) 732 | { 733 | $this->pushNotification("Informations of user has been changed!"); 734 | } 735 | 736 | private function pushNotification($message) 737 | { 738 | Log::info($message); 739 | } 740 | } 741 | ``` 742 | 743 | ### State Pattern 744 | 745 | - **Intent** 746 | - Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. 747 | - An object-oriented state machine (FSM) 748 | - Wrapper + Polymorphic wrapper + Collaboration 749 | - **When**: FSM-like logic is required to be implemented. 750 | - **Why**: To eliminate the problems of a switch...case statement, and to better encapsulate the meaning of each 751 | individual state. 752 | - **Check list** 753 | 1. Identify an existing class, or create a new class, that will serve as the "state machine" from the client's 754 | perspective. That class is the "wrapper" class. 755 | 2. Create a State base class that replicates the methods of the state machine interface. Each method takes one 756 | additional parameter: an instance of the wrapper class. The State base class specifies any useful "default" 757 | behavior. 758 | 3. Create a State derived class for each domain state. These derived classes only override the methods they need to 759 | override. 760 | 4. The wrapper class maintains a "current" State object. 761 | 5. All client requests to the wrapper class are simply delegated to the current State object, and the wrapper 762 | object's this pointer is passed. 763 | 6. The State methods change the "current" state in the wrapper object as appropriate. 764 | - **Example** 765 | 766 | ```php 767 | interface Context 768 | { 769 | public function next(); 770 | 771 | public function setState(State $state); 772 | } 773 | 774 | abstract class State 775 | { 776 | public abstract function next(); 777 | } 778 | 779 | class FirstState extends State 780 | { 781 | public function next(Context $context) 782 | { 783 | $context->setState(new SecondState); 784 | } 785 | } 786 | 787 | class SecondState extends State 788 | { 789 | public function next(Context $context) 790 | { 791 | $context->setState(new ThirdState); 792 | } 793 | } 794 | 795 | class ThirdState extends State 796 | { 797 | public function next(Context $context) 798 | { 799 | $context->setState(new ThirdState); 800 | } 801 | } 802 | 803 | /* 804 | * "Wrapper" class 805 | */ 806 | class Client implements Context 807 | { 808 | /* 809 | * @var State 810 | */ 811 | protected $currentState; 812 | 813 | public function __construct(State $initialState) 814 | { 815 | $this->setState($initialState); 816 | } 817 | 818 | public function next() 819 | { 820 | $this->currentState->next($this); 821 | } 822 | 823 | public function setState(State $state) 824 | { 825 | $this->currentState = $state; 826 | } 827 | } 828 | ``` 829 | 830 | ### Strategy Pattern 831 | 832 | - **When**: Flexibility and reusability is more important than simplicity. 833 | - **Why**: Use it to implement big, interchangeable chunks of complicated logic, while keeping a common algorithm 834 | signature. 835 | - **Check List** 836 | 1. Identify an algorithm (i.e. a behavior) that the client would prefer to access through a "flex point". 837 | 2. Specify the signature for that algorithm in an interface. 838 | 3. Bury the alternative implementation details in derived classes. 839 | 4. Clients of the algorithm couple themselves to the interface. 840 | - **Example** 841 | 842 | ```php 843 | interface Strategy 844 | { 845 | public function algorithm(); 846 | } 847 | 848 | class StrategyA 849 | { 850 | public function algorithm() 851 | { 852 | // Implement plan A 853 | } 854 | } 855 | 856 | class StrategyB 857 | { 858 | public function algorithm() 859 | { 860 | // Implement plan B 861 | } 862 | } 863 | 864 | class Stub 865 | { 866 | public function foo(Strategy $strategy) 867 | { 868 | $strategy->algorithm(); 869 | } 870 | } 871 | ``` 872 | 873 | ### Template Method Pattern 874 | 875 | - **Intent** 876 | - Define the skeleton of an algorithm in an operation, deferring some steps to client subclasses. Template Method 877 | lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. 878 | - Base class declares algorithm 'placeholders', and derived classes implement the placeholders. 879 | - **When**: Eliminate duplication in a simple way. 880 | - **Why**: There is duplication and flexibility is not a problem. 881 | - **Check List** 882 | 1. Examine the algorithm, and decide which steps are standard and which steps are peculiar to each of the current 883 | classes. 884 | 2. Define a new abstract base class to host the "don't call us, we'll call you" framework. 885 | 3. Move the shell of the algorithm (now called the "template method") and the definition of all standard steps to 886 | the new base class. 887 | 4. Define a placeholder or "hook" method in the base class for each step that requires many different 888 | implementations. This method can host a default implementation – or – it can be defined as abstract (Java) or 889 | pure virtual (C++). 890 | 5. Invoke the hook method(s) from the template method. 891 | 6. Each of the existing classes declares an "is-a" relationship to the new abstract base class. 892 | 7. Remove from the existing classes all the implementation details that have been moved to the base class. 893 | 8. The only details that will remain in the existing classes will be the implementation details peculiar to each 894 | derived class. 895 | - **Example** 896 | 897 | ```php 898 | abstract class Animal 899 | { 900 | public function run() 901 | { 902 | return 'Running'; 903 | } 904 | 905 | public function eat() 906 | { 907 | return 'Eating'; 908 | } 909 | } 910 | 911 | class Dog 912 | { 913 | public function woof() 914 | { 915 | return 'Woof Woof'; 916 | } 917 | } 918 | 919 | class Cat 920 | { 921 | public function meow() 922 | { 923 | return 'Meow...'; 924 | } 925 | } 926 | ``` 927 | 928 | ### Visitor Pattern 929 | 930 | - **When**: A decorator is not appropriate and some extra complexity is acceptable. 931 | - **Why**: To allow and organized approach to defining functionality for several objects but at the price of higher 932 | complexity. 933 | - **Check List** 934 | 1. Confirm that the current hierarchy (known as the `Element` hierarchy) will be fairly stable and that the public 935 | interface of these classes is sufficient for the access the Visitor classes will require. If these conditions are 936 | not met, then the Visitor pattern is not a good match. 937 | 2. Create a `Visitor` base class with a `visit(ElementXxx)` method for each `Element` derived type. 938 | 3. Add an `accept(Visitor)` method to the `Element` hierarchy. The implementation in each `Element` derived class is 939 | always the same – `accept( Visitor v ) { v.visit( this ); }`. Because of cyclic dependencies, the declaration of 940 | the `Element` and `Visitor` classes will need to be interleaved. 941 | 4. The `Element` hierarchy is coupled only to the `Visitor` base class, but the `Visitor` hierarchy is coupled to 942 | each `Element` derived class. If the stability of the `Element` hierarchy is low, and the stability of 943 | the `Visitor` hierarchy is high; consider swapping the 'roles' of the two hierarchies. 944 | 5. Create a `Visitor` derived class for each "operation" to be performed on `Element` objects. `visit()` 945 | implementations will rely on the Element's public interface. 946 | 6. The client creates `Visitor` objects and passes each to `Element` objects by calling `accept()`. 947 | - **Example** 948 | 949 | ```php 950 | interface Host 951 | { 952 | public function getStubs(); 953 | 954 | public function accept(Visitor $visitor); 955 | } 956 | 957 | interface Visitor 958 | { 959 | public function getStubs(); 960 | 961 | public function visit(Host $host); 962 | } 963 | 964 | class B implements Host 965 | { 966 | public function accept(Visitor $visitor) 967 | { 968 | $visitor->visit($this); 969 | } 970 | 971 | public function getStubs() 972 | { 973 | return 'stubs'; 974 | } 975 | } 976 | 977 | class A implements Visitor 978 | { 979 | private $stubs; 980 | 981 | public function visit(Host $b) 982 | { 983 | $this->stubs = $b->getStubs(); 984 | } 985 | 986 | /** 987 | * Get stubs of B 988 | */ 989 | public function getStubs() 990 | { 991 | return $this->stubs; 992 | } 993 | } 994 | ``` 995 | 996 | ### Gateway Pattern 997 | 998 | - **When**: When you need to retrieve or persist information. 999 | - **Why**: It offers a simple public interface for complicated persistence operations. It also encapsulates persistence 1000 | knowledge and decouples business logic from persistence logic. 1001 | - **Example** 1002 | 1003 | ```php 1004 | class A { } 1005 | 1006 | interface Gateway 1007 | { 1008 | public function persist(A $a); 1009 | 1010 | public function retrieve($id); 1011 | } 1012 | 1013 | class FileGateway implements Gateway 1014 | { 1015 | public function persist(A $a) 1016 | { 1017 | // TODO: Store serialized an A object to filesystem 1018 | } 1019 | 1020 | public function retrieve($id) 1021 | { 1022 | // TODO: Retrieve an A object by its id from filesystem 1023 | } 1024 | } 1025 | 1026 | class DbGateway implements Gateway 1027 | { 1028 | public function persist(A $a) 1029 | { 1030 | // TODO: Store serialized an A object to database 1031 | } 1032 | 1033 | public function retrieve($id) 1034 | { 1035 | // TODO: Store serialized an A object to database 1036 | } 1037 | } 1038 | ``` 1039 | 1040 | ### Active Object Pattern 1041 | 1042 | - **What**: The Active Object Pattern decouples the method invocation from method execution. 1043 | - **When**: Several similar objects have to execute with a single command. 1044 | - **Why**: It forces clients to perform a single task and affect multiple objects. 1045 | - **Example** 1046 | 1047 | ```php 1048 | class Message 1049 | { 1050 | private $content; 1051 | 1052 | public function __construct($content) 1053 | { 1054 | $this->content = $content; 1055 | } 1056 | } 1057 | 1058 | class Queue 1059 | { 1060 | private $dispatcher; 1061 | 1062 | private $messages = []; 1063 | 1064 | public function __construct(Dispatcher $dispatcher) 1065 | { 1066 | $this->dispatcher = $dispatcher; 1067 | } 1068 | 1069 | public function enqueue(Message $message) 1070 | { 1071 | $this->messages[] = $message; 1072 | 1073 | $this->dispatcher->notify(); 1074 | } 1075 | 1076 | public function dequeue() 1077 | { 1078 | return array_shift($this->messages); 1079 | } 1080 | } 1081 | 1082 | class Dispatcher 1083 | { 1084 | public function notify() 1085 | { 1086 | // Spawn new Servant for each method call 1087 | $servant = new Servant(); 1088 | $servant->start(); 1089 | 1090 | // Do another things 1091 | 1092 | $servant->wait(function ($type, $buffer) { 1093 | // Return job result 1094 | }); 1095 | } 1096 | } 1097 | 1098 | class Servant extends Process 1099 | { 1100 | // For example, we simply declare Servant extends Process from symfony/process package to it can run asynchronously. 1101 | } 1102 | 1103 | interface Proxy 1104 | { 1105 | public function visitUrl($url); 1106 | } 1107 | 1108 | class HttpProxy 1109 | { 1110 | private $queue; 1111 | 1112 | public function __construct(Queue $queue) 1113 | { 1114 | $this->queue = $queue; 1115 | } 1116 | 1117 | public function visitUrl($url) 1118 | { 1119 | $message = new Message($url); 1120 | 1121 | $this->queue->enqueue($message); 1122 | } 1123 | } 1124 | ``` 1125 | 1126 | ### Event Dispatcher Pattern 1127 | 1128 | - **Example** 1129 | 1130 | ```php 1131 | interface SenderInterface {} 1132 | 1133 | class EventManager 1134 | { 1135 | private $listeners = []; 1136 | 1137 | public function listen($event, $callback) 1138 | { 1139 | $this->listeners[$event][] = $callback; 1140 | } 1141 | 1142 | public function dispatch($event, SenderInterface $sender) 1143 | { 1144 | foreach($this->listeners[$event] as $listener) { 1145 | call_user_func($listener, $sender); 1146 | } 1147 | 1148 | } 1149 | } 1150 | 1151 | class BlogPublisher implements SenderInterface 1152 | { 1153 | private $eventManager; 1154 | private $title; 1155 | 1156 | public function __construct(EventManager $eventManager) 1157 | { 1158 | $this->eventManager = $eventManager; 1159 | } 1160 | 1161 | public function getTitle() 1162 | { 1163 | return $this->title; 1164 | } 1165 | 1166 | public function setTitle($title) 1167 | { 1168 | $this->title = $title; 1169 | $this->eventManager->dispatch('blog_title_update', $this); 1170 | return $this; 1171 | } 1172 | } 1173 | ``` -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dinhquochan/php-design-patterns", 3 | "description": "PHP Design Pattern Implementations", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Dinh Quoc Han", 8 | "email": "dqh@dinhquochan.com" 9 | } 10 | ], 11 | "require": { 12 | "php": "~7.0|~8.0" 13 | }, 14 | "autoload": { 15 | "psr-4": { 16 | "DesignPatterns\\": "src/" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/AbstractServer/RedRose.php: -------------------------------------------------------------------------------- 1 | sold = true; 12 | } 13 | 14 | public function isSold() 15 | { 16 | return $this->sold; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/AbstractServer/Rose.php: -------------------------------------------------------------------------------- 1 | rose = $rose; 13 | } 14 | 15 | public function sell() 16 | { 17 | $this->rose->sell(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/AbstractServer/YellowRose.php: -------------------------------------------------------------------------------- 1 | sold = true; 12 | } 13 | 14 | public function isSold() 15 | { 16 | return $this->sold; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/ActiveObject/MultipleFileUploader.php: -------------------------------------------------------------------------------- 1 | uploaders[] = $uploader; 12 | } 13 | 14 | public function run() 15 | { 16 | while (!empty($this->uploaders)) { 17 | $uploader = array_shift($this->uploaders); 18 | /** @var \DesignPatterns\ActiveObject\UploadCommand $uploader */ 19 | $uploader->execute(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/ActiveObject/UploadCommand.php: -------------------------------------------------------------------------------- 1 | size = $size; 21 | $this->chunk = $size / $speed; 22 | $this->multipleFileUploader = $multipleFileUploader; 23 | } 24 | 25 | public function execute() 26 | { 27 | $this->uploaded += $this->chunk; 28 | 29 | print "\n{$this->uploaded}/{$this->size}"; 30 | 31 | if ($this->uploaded < $this->size) { 32 | $this->multipleFileUploader->addUploader($this); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Adapter/ProductInterface.php: -------------------------------------------------------------------------------- 1 | rose = $rose; 15 | } 16 | 17 | public function getDescription() 18 | { 19 | return null; 20 | } 21 | 22 | public function getPrice() 23 | { 24 | return $this->rose->getPriceFromDatabase(); 25 | } 26 | 27 | public function getPicture() 28 | { 29 | return $this->rose->showImage(); 30 | } 31 | 32 | public function sell() 33 | { 34 | $this->rose->sell(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Adapter/TheOldRoseInterface.php: -------------------------------------------------------------------------------- 1 | approve()) { 10 | $payment->send(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Bridge/CreditPayment.php: -------------------------------------------------------------------------------- 1 | send(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Bridge/DirectPayment.php: -------------------------------------------------------------------------------- 1 | paymentSource = $paymentSource; 19 | } 20 | 21 | protected function sendImpl() 22 | { 23 | $this->paymentSource->send(); 24 | } 25 | 26 | protected function approveImpl() 27 | { 28 | $this->paymentSource->approve(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Bridge/PaymentSource.php: -------------------------------------------------------------------------------- 1 | getPaymentMethod(); 11 | 12 | $this->executePayment($paymentMethod); 13 | } 14 | 15 | private function executePayment(PaymentMethod $paymentMethod) 16 | { 17 | try { 18 | $paymentMethod->execute(); 19 | } catch (\Exception $e) { 20 | throw new PaymentProcessingException( 21 | "Paying with {$paymentMethod} has failed with error {$e->getMessage()}" 22 | ); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Command/PaypalPayment.php: -------------------------------------------------------------------------------- 1 | paymentMethod; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Command/VisaPayment.php: -------------------------------------------------------------------------------- 1 | orders[] = $order; 12 | } 13 | 14 | public function place() 15 | { 16 | foreach ($this->orders as $order) { 17 | /** @var \DesignPatterns\Composite\Order $order */ 18 | $order->place(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Composite/Order.php: -------------------------------------------------------------------------------- 1 |