Week 25 Sandbox

Object Oriented Design Patterns Book Switch

This past week I've been reading Head First Design Patterns as my primary learning resource and refresher for OO design patterns. I've found I don't click well at all with it's teaching style, though. I prefer a teaching method that first details foundation, rules, and theory, then gives brief practical example, preferably also with a following challenge problem for practice.

Head First, however, teaches largely by example, breaking up the foundations over long, drawn out, slowly built examples, sometimes starting with, "done the wrong way," examples. This style is too drawn out for me and too similiar to how online courses generally teach, in which I feel I lose too much foundational knowledge in exchange for time that could be better spent than on fifty page or four hour video examples. Thus, dropping this book for now, and moving to replacement, Dive Into Design Patterns instead.

Object Oriented Design Patterns Notes

Program to Interface, Not Implementation

Programming to implementation makes code rigid and with high cost of change. Responsibilities get mixed, dependancies grow many, and simplicity fades. A base design principle is to program to the interface instead of implementation. While concrete classes set details more in stone, interfaces provide a contract with many implementations. High-level classes no longer need to worry about what low-level classes do. All they care about is the interfaces used to access the logic of the lower-level classes.

The following shows the benefit of this principle, first with some "bad" tightly coupled, non-flexible code, followed by loosely coupled code, programmed to an interface.



          //AnimalFeeder.php

          //heavy coupling, many depedencies
          require_once 'Cat.php';
          require_once 'Dog.php';
          require_once 'Panda.php';

          class AnimalFeeder{
            private $dog;
            private $cat;
            private $panda;

            public function __construct(){
              //no flexibility in Animal type
              $this->dog = new Dog();
              $this->cat = new Cat();
              $this->panda = new Panda();
            }

            public function feedAnimals(){
              /*non-uniform and complex
              interface */
              $this->dog->eatKibble();
              $this->cat->eatFish();
              $this->panda->eatBamboo();
            }

          }

          //test.php

          require_once 'AnimalFeeder.php';

          $feeder = new AnimalFeeder();

          $feeder->feedAnimals();
          

          //Cat.php

          class Cat{
            public function eatFish(){
              echo "Cat is eating fish\n";
            }
          }

          //Dog.php

          class Dog{
            public function eatKibble(){
              echo "Dog is eating kibble\n";
            }
          }

          //Panda.php

          class Panda{
            public function eatBamboo(){
              echo "Panda is eating bamboo\n";
            }
          }

In the second example, now with cleaner code, note how both the high-level and low-level classes are both dependent on the interface. test.php references Animals and does not care what type of Animal they are. Likewise, all implementations of Animal are dependent on the Animal interface. This allows for high flexibility, extensibility, and results in clean, decoupled code



          class AnimalFeeder{
            private $animals = [];

            public function __construct(){
              /*allows any num of animal
               to be passed in*/
              $args = func_get_args();

              foreach($args as $animal){
                $this->animals[] = $animal;
              }
            }

            public function feedAnimals(){
              /*does not care about
              type of animal*/
              foreach($this->animals as $animal){
                  $animal->eat();
              }
            }

          }

            //test.php

            //Depenencies in endpoint instead of low-level
            require_once 'AnimalFeeder.php';
            require_once 'Cat.php';
            require_once 'Dog.php';
            require_once 'Panda.php';

            $dog = new Dog();
            $panda = new Panda();
            $cat = new Cat();

            //flexible args 1-n
            $feeder = new AnimalFeeder($dog, $panda, $cat);

            $feeder->feedAnimals();

            //Animal.php

            interface Animal{
              public function eat();
            }

            //Cat.php

            require_once 'Animal.php';

            class Cat implements Animal{
              public function eat(){
                echo "Cat is eating fish\n";
              }
            }

            //Dog.php

            require_once 'Animal.php';

            class Dog implements Animal{
              public function eat(){
                echo "Dog is eating kibble\n";
              }
            }

            //Dog.php

            require_once 'Animal.php';

            class Panda implements Animal{
              public function eat(){
                echo "Panda is eating bamboo\n";
              }
            }