└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # design-pattern-practice 2 | Practice notes for problems and solutions learned from Head First Design Pattern book 3 | 4 | [![In-progress](https://img.shields.io/badge/In-progress-%23FFac45.svg?&style=for-the-badge&logo=java&logoColor=white&color=yellow)](https://github.com/) 5 | 6 | 7 |
8 | 9 | ******* 10 | Tables of contents 11 | 1. [Strategy Pattern](#strategypattern) 12 | 2. [Observer Pattern](#observerpattern) 13 | 3. [Decorator Pattern](#decoratorpattern) 14 | 4. [Factory Pattern](#factorypattern) 15 | 5. [Singleton Pattern](#singletonpattern) 16 | 6. [Command Pattern](#commandpattern) 17 | 18 | ******* 19 | [Jump to end](#theend) 20 |
21 | 22 | # Strategy Pattern: 23 | 24 | **Get started:** Write a program to display a Duck and other kinds of duck. Basic action that a duck can perform is swim, quack and display 25 | 26 | **Naive approach:** Use OO basics like Inheritance, create a Duck then other kinds of duck inherits attributes, method and override it if needed. 27 | 28 | The class diagram looks like: 29 | 30 | ```mermaid 31 | 32 | classDiagram 33 | Duck <|-- OtherKindsOfDuck 34 | Duck <|-- MallardDuck 35 | Duck <|-- RedheadDuck 36 | 37 | class Duck{ 38 | +swim() 39 | +quack() 40 | +display() 41 | } 42 | class OtherKindsOfDuck{ 43 | +display() 44 | } 45 | class MallardDuck{ 46 | +display() 47 | } 48 | class RedheadDuck{ 49 | +display() 50 | } 51 | 52 | ``` 53 | 54 | 55 | **What if ..? :** 56 | - The customer wants to add a Rubber Duck. It's ok, just override all the methods then leave it empty 57 | - The customer wants to add new action to a duck, like fly. We can add fly method to Duck class, and we forget to override this method in 33 subclass of Duck that can not fly. Big problem here! 58 | - The customer wants different duck to quack differently, We have to override quack in every Duck subclass? 59 | 60 | **Another approach:** 61 | - Use Interface `Quackable` and `Flyable`. Which type of duck can flly or quack can implement from theese interfaces but this is still not a good approach. 62 | - This approach looks good at first, but not good for mantainance. When we need to modify a behavior, we need to track and change it in all subclasses, this can lead to new bug. 63 | 64 | ```mermaid 65 | classDiagram 66 | Duck <|-- RubberDuck 67 | Duck <|-- MallardDuck 68 | Duck <|-- RedheadDuck 69 | Quackable <.. RedheadDuck 70 | Quackable <.. MallardDuck 71 | 72 | class Duck{ 73 | +swim() 74 | +quack() 75 | +display() 76 | } 77 | class RubberDuck{ 78 | +display() 79 | } 80 | class MallardDuck{ 81 | +display() 82 | +quack() 83 | } 84 | class RedheadDuck{ 85 | +display() 86 | +fly() 87 | +quack() 88 | } 89 | 90 | class Quackable{ 91 | <> 92 | +quack() 93 | } 94 | 95 | Flyable <.. RedheadDuck 96 | class Flyable{ 97 | <> 98 | +fly() 99 | } 100 | 101 | ``` 102 | 103 | 104 | 105 | **Efficient approach:** 106 | - We will delegate our Duck methods to another class, and interface so that our program would be less code reuse but, more flexible and maintainable (Following some Design Principles noted at the end of this section) 107 | - Create interfaces for behaviors like FlyBehavior, QuackBehavior. Then for each specific behavior, we create a class that implements the interface. Next, add a reference of each behavior inside a kind of Duck. 108 | - Our final result: 109 | 110 | ```mermaid 111 | 112 | classDiagram 113 | Duck <|-- RubberDuck 114 | Duck <|-- MallardDuck 115 | Duck <|-- RedheadDuck 116 | 117 | class Duck{ 118 | -FlyBehavior flyBehavior 119 | -QuackBehavior quackBehavior 120 | +swim() 121 | +quack() 122 | +display() 123 | +setFlyBehavior() 124 | +setQuackBehavior() 125 | } 126 | 127 | class RubberDuck{ 128 | +display() 129 | } 130 | 131 | class MallardDuck{ 132 | +display() 133 | +quack() 134 | } 135 | 136 | class RedheadDuck{ 137 | +display() 138 | +fly() 139 | +quack() 140 | } 141 | FlyBehavior <.. FlyWithWings 142 | FlyBehavior <.. FlyNoWay 143 | 144 | class FlyBehavior{ 145 | <> 146 | +fly() 147 | } 148 | 149 | class FlyWithWings{ 150 | +fly() 151 | } 152 | 153 | class FlyNoWay{ 154 | +fly() 155 | } 156 | 157 | 158 | QuackBehavior <.. Quack 159 | QuackBehavior <.. Squeak 160 | 161 | class QuackBehavior{ 162 | <> 163 | +fly() 164 | } 165 | 166 | class Quack{ 167 | +quack() 168 | } 169 | 170 | class Squeak{ 171 | +quack() 172 | } 173 | 174 | ``` 175 | 176 | 177 | **Conclusion:** 178 | - **The Strategy Pattern** defines a family of algorithms,encapsulates each one, and makes them interchangeable.Strategy lets the algorithm vary independently from clients that use it 179 | 180 | [Back to top](#top) 181 | 182 |
183 | 184 | # Observer Pattern: 185 | **Get started:** Given that we need to write a Weather monitor application. In this application, we have to get data from a provider. The important thing is that, we need to notify to all users whenver the forecase measurement data is updated. And also we need to display it with in multiple forms. 186 | 187 | **Naive approach:** So first of all, we need a class contains the data and some methods to get and display data to the user. The class would look like: 188 | 189 | ```mermaid 190 | classDiagram 191 | 192 | class WeatherData{ 193 | -CurrentConditionDisplay currentConditionDisplay 194 | -StatisticsDisplay statisticsDisplay 195 | -ForecastDisplay forecastDisplay 196 | +getTemprature() 197 | +getHumidity() 198 | +getPressure() 199 | +measurementsDataChanged() 200 | } 201 | ``` 202 | 203 | And our `measurementsDataChanged` method would look something like: 204 | 205 | ```java 206 | public void measurementsDataChanged() { 207 | float temp = getTemperature() 208 | float humidity = getHumidity() 209 | float pressure = getPressure() 210 | 211 | currentConditionDisplay.update(temp, humidity, pressure) 212 | statisticsDisplay.update(temp, humidity, pressure) 213 | forecastDisplay.update(temp, humidity, pressure) 214 | } 215 | ``` 216 | 217 | 218 | **What if ..? :** 219 | - We need to add more display or remove one. Because the `measurementsDataChanged()` method is implemented in concrete way, we can not add or remove display withou making changes to the program 220 | 221 | **Efficient approach:** We use Observer Pattern for this case. In this pattern, we will have a `Subject` which is our `WeatherData` class, where we get data from. The next one is `Observers`, all the thing that consume the data which is our displays 222 | - First, we need to define all the interface needed for `Observers`, `Subjects` along with shared behavior of each observer: 223 | ```mermaid 224 | classDiagram 225 | class Subject{ 226 | <> 227 | +registerObserver(Observer o) 228 | +removeObserver(Observer o) 229 | +notifyObservers() 230 | } 231 | 232 | class Observer{ 233 | <> 234 | +update(float temp, float humidity, float pressure) 235 | } 236 | 237 | class DisplayElement{ 238 | <> 239 | +display() 240 | } 241 | 242 | ``` 243 | - Next, refactor our `WeatherData` class: 244 | ```java 245 | public class WeatherData implements Subject { 246 | private ArrayList observers; 247 | private float temperature; 248 | private float humidity; 249 | private float pressure; 250 | 251 | public WeatherData() { 252 | observers = new ArrayList(); 253 | } 254 | 255 | public void registerObserver(Observer o) { 256 | observers.add(o); 257 | } 258 | 259 | public void removeObserver(Observer o) { 260 | int i = observers.indexOf(o); 261 | if (i >= 0) { 262 | observers.remove(i); 263 | } 264 | } 265 | 266 | public void notifyObservers() { 267 | for (int i = 0; i < observers.size(); i++) { 268 | Observer observer = (Observer) observers.get(i); 269 | observer.update(temperature, humidity, pressure); 270 | } 271 | } 272 | 273 | public void measurementsChanged() { 274 | notifyObservers(); 275 | } 276 | public void setMeasurements(float temperature, float humidity, float pressure) { 277 | this.temperature = temperature; 278 | this.humidity = humidity; 279 | this.pressure = pressure; 280 | measurementsChanged(); 281 | } 282 | } 283 | ``` 284 | 285 | 286 | - Our final result: 287 | 288 | ```mermaid 289 | classDiagram 290 | WeatherData <|-- Subject 291 | class WeatherData{ 292 | -CurrentConditionDisplay currentConditionDisplay 293 | -StatisticsDisplay statisticsDisplay 294 | -ForecastDisplay forecastDisplay 295 | +getTemprature() 296 | +getHumidity() 297 | +getPressure() 298 | +measurementsDataChanged() 299 | +registerObserver(Observer o) 300 | +removeObserver(Observer o) 301 | +notifyObservers() 302 | } 303 | class Subject{ 304 | <> 305 | +registerObserver(Observer o) 306 | +removeObserver(Observer o) 307 | +notifyObservers() 308 | } 309 | 310 | class Observer{ 311 | <> 312 | +update(float temp, float humidity, float pressure) 313 | } 314 | 315 | class DisplayElement{ 316 | <> 317 | +display() 318 | } 319 | 320 | CurrentConditionDisplay <|-- Observer 321 | CurrentConditionDisplay <|-- DisplayElement 322 | class CurrentConditionDisplay { 323 | +update(float temp, float humidity, float pressure) 324 | +display() 325 | } 326 | 327 | StatisticsDisplay <|-- Observer 328 | StatisticsDisplay <|-- DisplayElement 329 | class StatisticsDisplay { 330 | +update(float temp, float humidity, float pressure) 331 | +display() 332 | } 333 | 334 | ForecastDisplay <|-- Observer 335 | ForecastDisplay <|-- DisplayElement 336 | class ForecastDisplay { 337 | +update(float temp, float humidity, float pressure) 338 | +display() 339 | } 340 | ``` 341 | **Conclusion:** 342 | - **The Observer Pattern** defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically 343 | 344 | [Back to top](#top) 345 | 346 |
347 | 348 | # Decorator Pattern: 349 | **Get started:** Write a program to for a coffee shop to create order with many beverages, of course, many toppings and other properties are added onto each beverage. 350 | 351 | **Naive approach:** Create an abstract class called Beverage, and other kind will extends this class. Then just override methods like cost() to calculate the price for each kind of beverage. 352 | 353 | The class diagram looks like: 354 | ```mermaid 355 | classDiagram 356 | class Beverage{ 357 | -String description 358 | +getDescription() 359 | +cost() 360 | } 361 | 362 | Beverage <|-- HouseBlend 363 | class HouseBlend{ 364 | +cost() 365 | } 366 | 367 | Beverage <|-- DarkRoast 368 | class DarkRoast{ 369 | +cost() 370 | } 371 | 372 | Beverage <|-- Decaf 373 | class Decaf{ 374 | +cost() 375 | } 376 | 377 | 378 | Beverage <|-- Espresso 379 | class Espresso{ 380 | +cost() 381 | } 382 | ``` 383 | 384 | **What if ..? :** 385 | - We want to add some condiments like milk, chocolate, then we just need to create more class for this kind of "new" beverage. Maybe for current Coffee type, we would have new classes such as CoffeeWithMilk, CoffeeWithSoy,..**uh oh Class Explosion** 386 | - We calculate the price for each beverage, with different condiments, size 387 | 388 | **Another approach:** 389 | - Put all the condiments inside the abstract Beverage class, then for each subclass, we have to override all the checking condiments method as below. This is still not a good approach 390 | 391 | ```mermaid 392 | classDiagram 393 | class Beverage{ 394 | -String description 395 | -Condiment milk 396 | -Condiment mocha 397 | -Condiment soy 398 | -Condiment whip 399 | +getDescription() 400 | +cost() 401 | +hasMilk() 402 | +setMilk() 403 | +hasMocha() 404 | +setMocha() 405 | +hasWhip() 406 | +setWhip() 407 | } 408 | 409 | Beverage <|-- HouseBlend 410 | class HouseBlend{ 411 | +cost() 412 | } 413 | 414 | Beverage <|-- DarkRoast 415 | class DarkRoast{ 416 | +cost() 417 | } 418 | 419 | Beverage <|-- Decaf 420 | class Decaf{ 421 | +cost() 422 | } 423 | 424 | Beverage <|-- Espresso 425 | class Espresso{ 426 | +cost() 427 | } 428 | ``` 429 | - Our `cost()` method in `Beverage` class would look like: 430 | ```java 431 | public double cost() { 432 | double condimentCost = 0.0 433 | if (hasMilk()) { 434 | condimentCost += milk.getCost() 435 | } 436 | 437 | if (hasMocha()) { 438 | coondimentCost += mocha.getCost() 439 | } 440 | return condimentCost 441 | } 442 | ``` 443 | 444 | - OUr `cost()` method in concreate class lets say `DarkRoast` would loook like: 445 | 446 | ```java 447 | public double cost() { 448 | return 1.99 + super.cost() 449 | } 450 | ``` 451 | 452 | **Efficient approach:** WE will use the Decorator Pattern. The core concept of this pattern would look like: 453 | ```mermaid 454 | classDiagram 455 | class Component{ 456 | +methodA() 457 | +methodB() 458 | } 459 | 460 | Component <|-- ConcreteComponent 461 | class ConcreteComponent{ 462 | +methodA() 463 | +methodB() 464 | } 465 | 466 | Component <|-- Decorator 467 | class Decorator{ 468 | +methodA() 469 | +methodB() 470 | } 471 | 472 | Decorator <|-- ConcreteDecoratorA 473 | class ConcreteDecoratorA{ 474 | +methodA() 475 | +methodB() 476 | } 477 | 478 | Decorator <|-- ConcreteDecoratorB 479 | class ConcreteDecoratorB{ 480 | +methodA() 481 | +methodB() 482 | } 483 | ``` 484 | - When we apply to our current design, it would look like: 485 | ```mermaid 486 | classDiagram 487 | class Beverage{ 488 | -String description 489 | +cost() 490 | +getDescription() 491 | } 492 | 493 | Beverage <|-- HouseBlend 494 | class HouseBlend{ 495 | +cost() 496 | } 497 | 498 | Beverage <|-- Espresso 499 | class Espresso{ 500 | +cost() 501 | } 502 | 503 | Beverage <|-- DarkRoast 504 | class DarkRoast{ 505 | +cost() 506 | } 507 | 508 | Beverage <|-- CondimentDecorator 509 | class CondimentDecorator{ 510 | +getDescription() 511 | } 512 | 513 | CondimentDecorator <|-- Milk 514 | class Milk{ 515 | -Beverage beverage 516 | +cost() 517 | +getDescription() 518 | } 519 | 520 | CondimentDecorator <|-- Mocha 521 | class Mocha{ 522 | -Beverage beverage 523 | +cost() 524 | +getDescription() 525 | } 526 | 527 | CondimentDecorator <|-- Soy 528 | class Soy{ 529 | -Beverage beverage 530 | +cost() 531 | +getDescription() 532 | } 533 | 534 | CondimentDecorator <|-- Whip 535 | class Whip{ 536 | -Beverage beverage 537 | +cost() 538 | +getDescription() 539 | } 540 | ``` 541 | 542 | - Here is how we actually use this pattern in code: 543 | 544 | ```java 545 | public abstract class Beverage { 546 | String description = “Unknown Beverage”; 547 | public String getDescription() { 548 | return description; 549 | } 550 | public abstract double cost(); 551 | } 552 | 553 | public abstract class CondimentDecorator extends Beverage { 554 | public abstract String getDescription(); 555 | } 556 | ``` 557 | - Our concrete beverages: 558 | ```java 559 | public class Espresso extends Beverage { 560 | public Espresso() { 561 | description = “Espresso”; 562 | } 563 | public double cost() { 564 | return 1.99; 565 | } 566 | } 567 | ``` 568 | 569 | ```java 570 | public class HouseBlend extends Beverage { 571 | public HouseBlend() { 572 | description = “House Blend Coffee”; 573 | } 574 | public double cost() { 575 | return .89; 576 | } 577 | } 578 | ``` 579 | - Our condiment decorator 580 | ```java 581 | public class Mocha extends CondimentDecorator { 582 | Beverage beverage; 583 | public Mocha(Beverage beverage) { 584 | this.beverage = beverage; 585 | } 586 | public String getDescription() { 587 | return beverage.getDescription() + “, Mocha”; 588 | } 589 | public double cost() { 590 | return .20 + beverage.cost(); 591 | } 592 | } 593 | ``` 594 | - And finally how we decorate objects in runtime: 595 | ```java 596 | public static void main(String args[]) { 597 | Beverage beverage = new Espresso(); 598 | System.out.println(beverage.getDescription() + 599 | “$” + beverage.cost()); 600 | Beverage beverage2 = new DarkRoast(); 601 | beverage2 = new Mocha(beverage2); 602 | beverage2 = new Mocha(beverage2); 603 | beverage2 = new Whip(beverage2); 604 | System.out.println(beverage2.getDescription() + 605 | “$” + beverage2.cost()); 606 | Beverage beverage3 = new HouseBlend(); 607 | beverage3 = new Soy(beverage3); 608 | beverage3 = new Mocha(beverage3); 609 | beverage3 = new Whip(beverage3); 610 | System.out.println(beverage3.getDescription() + 611 | “$” + beverage3.cost()); 612 | } 613 | ``` 614 | - The output looks like: 615 | 616 | ``` 617 | Espresso $1.99 618 | Dark Roast Coffee, Mocha, Mocha, Whip $1.49 619 | House Blend Coffee, Soy, Mocha, Whip $1.34 620 | ``` 621 | 622 | [Back to top](#top) 623 | 624 |
625 | 626 | # Factory Pattern: 627 | - Simple Factory 628 | - Factory Method 629 | - Abstract Factory 630 | 631 | **Get started:** Write a program to support a pizza shop make their order process mroe smooth. This program supports creating many types of Pizza and be able to customized based on branches. 632 | 633 | 634 | **Naive approach:** As usual, we will come up with a `orderPizza` function like this, because to order a pizza, we need some mroe steps like prepare, bake, cut,... so we have: 635 | 636 | ```java 637 | Pizza orderPizza(){ 638 | Pizza pizza = new Pizza(); 639 | 640 | pizza.perpare(); 641 | pizza.bake(); 642 | pizze,cut(); 643 | pizza.box(); 644 | return pizza; 645 | } 646 | ``` 647 | 648 | The class diagram looks like: 649 | 650 | **What if ..? :** 651 | - We need to add more pizze types. Then our `orderPizza` becomes: 652 | ```java 653 | Pizza orderPizza(String type){ 654 | Pizza pizza; 655 | 656 | if (type.equals("cheese") pizza = new CheesePizza(); 657 | else if (type.equals("geekl") pizza = new GeekPizza(); 658 | else if (type.equals("cheese") pizza = new CheesePizza(); 659 | else if (type.equals("clam") pizza = new ClamPizza(); 660 | 661 | pizza.perpare(); 662 | pizza.bake(); 663 | pizze,cut(); 664 | pizza.box(); 665 | return pizza; 666 | } 667 | ``` 668 | 669 | **Another approach:** 670 | - We can improve the code inside `orderPizza` function. AS you can see, we can separate the code for creating a pizza based on its type. Put the creation code to a separated class, and we have a simple factory. **This is not actually a Design Pattern but commonly used** 671 | ```java 672 | public class SimplePizzaFactory { 673 | 674 | public Pizza createPizza(String type) { 675 | Pizza pizza; 676 | 677 | if (type.equals("cheese") pizza = new CheesePizza(); 678 | else if (type.equals("geekl") pizza = new GeekPizza(); 679 | else if (type.equals("cheese") pizza = new CheesePizza(); 680 | else if (type.equals("clam") pizza = new ClamPizza(); 681 | 682 | return pizza; 683 | } 684 | } 685 | ``` 686 | 687 | - The code for the PizzaStore class would look like: 688 | 689 | ```java 690 | public class PizzaStore { 691 | SimplePizzaFactory factory; 692 | public PizzaStore(SimplePizzaFactory factory) { 693 | this.factory = factory; 694 | } 695 | public Pizza orderPizza(String type) { 696 | Pizza pizza; 697 | 698 | pizza = factory.createPizza(type); 699 | pizza.perpare(); 700 | pizza.bake(); 701 | pizze,cut(); 702 | pizza.box(); 703 | 704 | return pizza; 705 | } 706 | 707 | } 708 | ``` 709 | - The class diagram looks like: 710 | 711 | ```mermaid 712 | classDiagram 713 | SimplePizzaFactory *-- PizzaStore 714 | class PizzaStore{ 715 | -SimplePizzaFactory factory 716 | +orderPizza() 717 | } 718 | 719 | class Pizza{ 720 | +prepare() 721 | +bake() 722 | +cut() 723 | +box() 724 | } 725 | 726 | Pizza <|-- CheesePizza 727 | class CheesePizza{ 728 | } 729 | 730 | Pizza <|-- GeekPizza 731 | class GeekPizza{ 732 | } 733 | 734 | Pizza <|-- PeperoniPizza 735 | class PeperoniPizza{ 736 | } 737 | 738 | Pizza <-- SimplePizzaFactory 739 | class SimplePizzaFactory{ 740 | -Pizza factory 741 | +createPizza() 742 | } 743 | 744 | class SimplePizzaFactory{ 745 | -Pizza factory 746 | +createPizza() 747 | } 748 | 749 | ``` 750 | **Problem occurs:** 751 | - When we need to fanchise our PizzaStore to many places with many different types of Pizza. For example. New York style pizza, Chicago style Pizza. Now we need to build a framework to create the right type of pizza and also keep our process smooth. Because our Pizza Store is already stable. 752 | - A naive approach is that we can create factory object for each fanchise. For example: NYPizzaFactory, ChicagoPizzaFactory and whenever we need, just `new NYPizzaFactory()` 753 | 754 | **Better approach** 755 | - Make our PizzaStore class and the `createPizza` method `abstract` as well. Because our ordering process is stable so we just keep it as usual. If you want every fanchise has the same process, make it `final`. 756 | - Implement orderPizze method 757 | - Create other franchise pizza factory by subclass from PizzaStore, and override the createPizza method. Because our createrPizze is abstract, we force all concreate class override it and let subclass decide what type of pizza they create. 758 | The code: 759 | ```java 760 | public abstract class PizzaStore { 761 | public Pizza orderPizza(String type) { 762 | Pizza pizza; 763 | 764 | pizza = factory.createPizza(type); 765 | pizza.perpare(); 766 | pizza.bake(); 767 | pizze,cut(); 768 | pizza.box(); 769 | 770 | return pizza; 771 | } 772 | 773 | abstract Pizza createPizza(String type); 774 | 775 | } 776 | ``` 777 | 778 | - Class diagram for franchise would look like: 779 | 780 | ```mermaid 781 | classDiagram 782 | class PizzaStore{ 783 | +createPizza() 784 | +orderPizza() 785 | } 786 | 787 | PizzaStore <|-- NewYorkStylePizzaStore 788 | class NewYorkStylePizzaStore{ 789 | +createPizza() 790 | } 791 | 792 | PizzaStore <|-- ChicagoStylePizzaStore 793 | class ChicagoStylePizzaStore{ 794 | +createPizza() 795 | } 796 | ``` 797 | - In each subclass of PizzaStore, let's say ChicagoStylePizzaStore, the `createPizze` method would look like: 798 | 799 | ```java 800 | public class ChicagoStylePizzaStore extends PizzaStore { 801 | Pizza createPizza(String item) { 802 | if (item.equals(“cheese”)) { 803 | return new NYStyleCheesePizza(); 804 | } else if (item.equals(“veggie”)) { 805 | return new NYStyleVeggiePizza(); 806 | } else if (item.equals(“clam”)) { 807 | return new NYStyleClamPizza(); 808 | } else if (item.equals(“pepperoni”)) { 809 | return new NYStylePepperoniPizza(); 810 | } else return null; 811 | } 812 | } 813 | ``` 814 | The `createPizza` method above is called Factory method. Which is: 815 | - Abstract so that the subclass is counted to handle object creation 816 | - Returns data type used within methods declare in the superclass 817 | - Isolates the code in superclass from knowing what kind of object is actually created 818 | 819 | **Efficient approach:** Currently, our Pizza Store is fine. However, it is very hard to keep the ingredients of the pizza in every store at different regions the same. For example, the New Your store would use another ingredients for the tomato sauce, the Chicago store also do so. This is when we need to do something to make our ingredients set more flexible across region. 820 | Because of the fact that New York would use one set of ingredients, Chicago would use another. It's time we need to create families of ingredients. 821 | First, we need to create an IngredientsFactory to create each set of ingredients. If our stores in every region have the same behavior, consider using an `abstract` class. In this case, an `interface` is good enough. Our shared factory would look like: 822 | 823 | ```java 824 | public interface PizzaIngredientFactory { 825 | public Dough createDough(); 826 | public Sauce createSauce(); 827 | public Cheese createCheese(); 828 | public Veggies[] createVeggies(); 829 | public Pepperoni createPepperoni(); 830 | public Clams createClam(); 831 | } 832 | ``` 833 | Next, we need to: 834 | - Build factory for each region 835 | - Implement set of ingredients class to be used with the factory 836 | - Hook the new factories to current Pizza Store 837 | 838 | Let's take the New York style for an example: 839 | ```java 840 | public class NYPizzaIngredientFactory implements PizzaIngredientFactory { 841 | public Dough createDough() { 842 | return new ThinCrustDough(); 843 | } 844 | public Sauce createSauce() { 845 | return new MarinaraSauce(); 846 | } 847 | public Cheese createCheese() { 848 | return new ReggianoCheese(); 849 | } 850 | public Veggies[] createVeggies() { 851 | Veggies veggies[] = { 852 | new Garlic(), 853 | new Onion(), 854 | new Mushroom(), 855 | new RedPepper() 856 | }; 857 | return veggies; 858 | } 859 | public Pepperoni createPepperoni() { 860 | return new SlicedPepperoni(); 861 | } 862 | public Clams createClam() { 863 | return new FreshClams(); 864 | } 865 | } 866 | ``` 867 | Remember that all the classed that being created should be the concrete class of the return type :) 868 | After that, our Pizza class becomes: 869 | 870 | ```java 871 | public abstract class Pizza { 872 | String name; 873 | Dough dough; 874 | Sauce sauce; 875 | Veggies veggies[]; 876 | Cheese cheese; 877 | Pepperoni pepperoni; 878 | Clams clam; 879 | abstract void prepare(); 880 | void bake() { 881 | System.out.println(“Bake for 25 minutes at 350”); 882 | } 883 | void cut() { 884 | System.out.println(“Cutting the pizza into diagonal slices”); 885 | } 886 | void box() { 887 | System.out.println(“Place pizza in official PizzaStore box”); 888 | } 889 | } 890 | ``` 891 | Now that we have an abstraction of Pizza. So clean! Let's take a look on it's concrete classes: 892 | ```java 893 | public class CheesePizza extends Pizza { 894 | PizzaIngredientFactory ingredientFactory; 895 | public CheesePizza(PizzaIngredientFactory ingredientFactory) { 896 | this.ingredientFactory = ingredientFactory; 897 | } 898 | void prepare() { 899 | System.out.println(“Preparing“ + name); 900 | dough = ingredientFactory.createDough(); 901 | sauce = ingredientFactory.createSauce(); 902 | cheese = ingredientFactory.createCheese(); 903 | } 904 | } 905 | 906 | 907 | public class ClamPizza extends Pizza { 908 | PizzaIngredientFactory ingredientFactory; 909 | public ClamPizza(PizzaIngredientFactory ingredientFactory) { 910 | this.ingredientFactory = ingredientFactory; 911 | } 912 | void prepare() { 913 | System.out.println(“Preparing“ + name); 914 | dough = ingredientFactory.createDough(); 915 | sauce = ingredientFactory.createSauce(); 916 | cheese = ingredientFactory.createCheese(); 917 | clam = ingredientFactory.createClam(); 918 | } 919 | } 920 | 921 | ``` 922 | 923 | 924 | **Conclusion:** 925 | - **The Factory Method Pattern**: defines an interface for creating object but let subclass decide which class to instantiate. 926 | - **Dependency Inversion Principle**: depends uppon abstractions. Do not depend on concrete classes 927 | 928 | > **Note**: Guildline to follow Dependency Inversion Principle: 929 | > - No variable should hold a reference to a concrete class (If use `new`, which means holding a reference to concrete class. Should delegate to a factory) 930 | > - No class should derive from concrete class (If we derive from concrete class, we depends on it. Only depends on abstractions like abstract class or interface) 931 | > - No method should override implemented method of base class (If we override implemented method, the base class wasn't really an abstraction anymore. Those implemented methods are meant to be shared by all subclasses) 932 | 933 | [Back to top](#top) 934 | 935 |
936 | 937 | # Singleton Pattern: 938 | **Get started:** We need to create an object to be used everywhere. Make sure this object is one-of-a-kind. 939 | 940 | **Naive approach:** Create a class as the diagram below. Because we only need our object is only initialized when we need it, then we check null before returning initialize it. So simple: 941 | ```mermaid 942 | classDiagram 943 | class Singleton{ 944 | - uniqueInstance: Singleton 945 | +getInstance(): Singleton 946 | } 947 | ``` 948 | 949 | ```java 950 | public class Singleton { 951 | private static Singleton uniqueInstance; 952 | // other useful instance variables here 953 | private Singleton() {} 954 | public static Singleton getInstance() { 955 | if (uniqueInstance == null) { 956 | uniqueInstance = new Singleton(); 957 | } 958 | return uniqueInstance; 959 | } 960 | // other useful methods here 961 | } 962 | ``` 963 | **Problem occurs:** 964 | - What if we have more than one place use the singleton at the same time? More than one thread access to properties of the Singleton. 965 | - How do we handle this case? Some threads would have some conditions for the properties from Singleton to make the program run properly. Value of a property might be modified by a process while another is about to use the previous value. 966 | - If we do not handle this case for multi threads, it would lead to the situation where one thread always run wrongly and never give us the expected behavior 967 | 968 | **Better approach:** We need to do something to make sure that 'if a thread is about to access singleton, every other threads that access before should done their job'. 969 | We can do this by using `synchronized` keyword in Java: 970 | 971 | ```java 972 | public class Singleton { 973 | private static Singleton uniqueInstance; 974 | // other useful instance variables here 975 | private Singleton() {} 976 | public static synchronized Singleton getInstance() { 977 | if (uniqueInstance == null) { 978 | uniqueInstance = new Singleton(); 979 | } 980 | return uniqueInstance; 981 | } 982 | // other useful methods here 983 | } 984 | ``` 985 | > **Note**: The `synchronized` is only relevent by the time we initlaized the object. When we already have the object, we do not need to synchronize this method. Looks like synchronized is unnedded overhead 986 | 987 | **Efficient approach**: We have 3 options to consider to make sure our Singleton object works properly for every cases including multithreading one. 988 | 1. Keep the `synchronized` and leave the current one as is if the object initalization is not expensive to your application 989 | 2. Move to an eagerly created instance rather than a lazily one. 990 | Using this approach, we rely on the JVM to create the unique instance of the Singleton when the class is loaded. The JVM guarantees that the instance will be created before any thread accesses the static uniqueInstance variable 991 | ```java 992 | public class Singleton { 993 | private static Singleton uniqueInstance = new Singleton(); 994 | private Singleton() {} 995 | public static Singleton getInstance() { 996 | return uniqueInstance; 997 | } 998 | } 999 | ``` 1000 | 3. Use “double-checked locking” to reduce the use of synchronization in getInstance() 1001 | With double-checked locking, we first check to see if an instance is created, and if not, THEN we 1002 | synchronize. This way, we only synchronize the first time through, just what we want 1003 | ```java 1004 | public class Singleton { 1005 | private volatile static Singleton uniqueInstance; 1006 | private Singleton() {} 1007 | public static Singleton getInstance() { 1008 | if (uniqueInstance == null) { 1009 | synchronized(Singleton.class) { 1010 | if (uniqueInstance == null) { 1011 | uniqueInstance = new Singleton(); 1012 | } 1013 | } 1014 | } 1015 | return uniqueInstance; 1016 | } 1017 | } 1018 | ``` 1019 | > **Note**: The volatile keyword ensures that multiple threads handle the uniqueInstance variable correctly when it is being initialized to the Singleton instance 1020 | 1021 | **Conclusion:** 1022 | - **The Singleton Pattern** : ensures a class has only one instance, and provides a global point of access to it 1023 | 1024 | 1025 | [Back to top](#top) 1026 | 1027 |
1028 | 1029 | # Command Pattern: 1030 | **Get started:** Given that we want to build an API to support a remote control to control our smart home devices. This API would allow the remote to interact with the classes provided by the devices vendor. In general, this control will have some slots, each slot would allow you to control a device. For each device, you can perform turn on, turn off and other actions. 1031 | The classes provided are something like: 1032 | 1033 | **Initial thought**: With OO Principles in our hands, our initial though is that we should do something to make sure that the remote does not know much about the details of how to turn on or turn off a light. It should request throught another helper to make it do it. 1034 | We also need to avoid if statements like `if slot1 == Light then turnOff() else if slot1 == GarageDoor then scrollDown()`. This approach will lead to lot of changes when we need to add more devices. More work and more bugs. 1035 | 1036 | **Approach:** Alright, now we have to do some thing to make the remote coltrol requests for what it want and another helper make the work done. This is quite similar to the way we request an order in restaurant. Let's take this as an example before we jump in to the pattern. 1037 | The order process looks like: 1038 | ```mermaid 1039 | flowchart LR 1040 | Customer-- createOrder -->Waitress --takeOrder, orderUp-->Cook--makeBurger, makeDrink -->Dish 1041 | ``` 1042 | The waitress will receive order from customer, then bring it to the Cook, tell him to cook. After that, the cook will make the dishes following the list inside the order. 1043 | Back to our case, the remote control need to create requests to `another class` to execute, and inside the execute of `another class`, we will call the exact method of each class provided from vendors. 1044 | Let's take a look at this diagram: 1045 | 1046 | ```mermaid 1047 | classDiagram 1048 | 1049 | Receiver <-- Client 1050 | ConcreteCommand <-- Client 1051 | class Client{ 1052 | } 1053 | 1054 | Receiver <-- ConcreteCommand 1055 | class Receiver{ 1056 | } 1057 | 1058 | Command <-- Invoker 1059 | class Invoker{ 1060 | } 1061 | 1062 | class Command{ 1063 | <> 1064 | +execute() 1065 | +undo() 1066 | } 1067 | Command <.. ConcreteCommand 1068 | class ConcreteCommand{ 1069 | +execute() 1070 | +undo() 1071 | } 1072 | ``` 1073 | In general, this is how it works: 1074 | 1075 | ```mermaid 1076 | flowchart LR 1077 | Client-- createCommandObject, setCommand --> Invoker --execute--> Receiver 1078 | ``` 1079 | 1080 | [Back to top](#top) 1081 | 1082 |
1083 | # Adapter and Facade Pattern: 1084 | 1085 | 1086 |
1087 | 1088 | 1089 | --------------------------------------------------------------------------------