2020-03-19 12:51:33 +05:30

7.9 KiB

LLD 2

  • Do you understand what a class is?
  • Why we use OOP
  • What an object is?
    • an instance of a class.
    • a physical entity that has some snapshot of the values of the attributes of the class

Builder Pattern

  1. class Bird {
        String color
        public Bird(String color) {
            this.color = color;
        }
    }
    
    Bird b = new Bird(); // how to create an object
    Bird b = new Bird("green"); // how to set attributes initially
    
  2. Now let's say that we wanted to add attributes to the class

    int height, weight;
    
  3. Do we need to change the way we're invoking the constructor? Because we now want to pass the height and weight too?

    public Bird(String color, int height, int weight) {}
    
  4. Now let's say we wanted to add 10 more attributes. What change would you have to do?

  5. Use a longer constructor.

  6. Is that a good idea? What can do wrong with that?

    • constuctor becomes huge
    • increases complexity, hence increases errors. Might confuse the order of variables. Might forget to pass some. Might pass incorrect values.
    • So, never write a function that takes in so many parameters.
  7. There is an even bigger problem. Identify? Backwards compatibility. Existing code breaks! Explain how with example.

  8. Solution 1:

    • Have multiple constructors
    • constructor overloading
    • old code continues to use old constructor. New code uses new constructor
  9. Issue? Can't choose which attribute to set. Must set all attributes.

  10. Bad Solution 1

    • Make constructor for every possible combination
    • how many constructors? 2^n
    • Adding any attribute is a nightmare
  11. Bad solution 2

    • pass an object
      public Bird(Attributes obj) {
          this.weight = obj.weight;
          ...
      }
      
    • same problem now repeated for obj
    • So, doesn't help
  12. Builder Pattern

    public class Bird {
        int height, weight;
        String color;
    
        // set to private to enforce builder
        public Bird(Builder builder) {
            // same as bad solution 2, but Builder is special
            this.height = builder.height;
            this.weight = builder.weight;
            this.color = builder.color;
        }
    
        // -------------------- Builder --------------------
        // explain why static
        public static class Builder {
            int height, weight;
            String color;
            public Builder(); // don't pass values in constructor
            public Builder setHeight(int height) {
                this.height = height;
                return this; // explain the return type
            }
            public Builder setWidth(int width) {
                this.width = width;
                return this;
            }
            public Builder setColor(String color) {
                this.color = color;
                return this;
            }
            public Bird build() {
                // because we need an instance of Bird
                // and not an instance of Builder
                Bird b = new Bird(this);
                return b;
            }
        }
        // --------------------------------------------------
    }
    
    Bird b = new Bird.Builder()
                     .setHeight(10)
                     .setColor("red")
                     // can skip some variable - weight
                     .build();
    
  13. Client can now initialize Bird in whatever order, using whatever attributes they want.

  14. What if we now wanted to add an attribute?

  15. Simply put the attribute in class and Builder. Add one line in contructor. Add one method in Builder

  16. Benefits:

    • Simpler to use
    • Backwards Compatible
  17. This is in Java, C++. Languages like Python have something else.

  18. How to now enforce some attribute?

  19. Put it in the builder constructor

    public Builder(int height) {
        this.height = height;
    }
    

    Client can still change the height later.

  20. How to enforce Builder? make Bird constructor private

  21. This is called Builder Pattern.

  22. Useful for classes with lots of attributes, and whose attribute requirements can be changed.


Stateful vs Stateless

// Stateful
public class Adder {
    int a;
    int b;
    public int add() {
        return this.a + this.b;
    }
}

Adder a = new Adder(5, 6);
int result = a.add();

// Stateless  - draw on RHS
public class Adder {
    public static int add(int a, int b) {
        return a + b;
    }
}

int result = Adder.add(5, 6);
  • What is better? Stateful or Stateless? todo: -50
  • Math.max, System.out - stateless

Address Validation

  • useful in lots of companies, especially ecommerce sites like amazon
  • if while placing an order, if you put the city as Mumbai but the pin as Delhi, will it accept? Should it accept?
  • why important - must validate the address before the order is shipped
class Address {
    addressLines;
    city;
    state;
    zip;
}
  • algorithm to validate the address lines would be much different from the one which validates the zip
  • zip is only a few chars. Address line can be huge
  • Different Class for each.
    • Because can have multiple logic depending on country / state
    • namespace
  • component level validators:
    • AddressLinesValidator
    • ZipValidator
    • SateValidator
    • CityValidator
  • top level validator:
    public class AddressValidator {
        public bool validate(Address a) {
            return (
                AddressLinesValidator.validate(a.addressLines)
                && ZipValidator.validate(a.zip)
                && StateValidator.validate(a.state)
                && CityValidator.validate(a.city)
            );
        }
    }
    
  • Let's try to write for a component level validator
    public class ZipValidator {
        public book validate(String zip) {
            // some logic here
        }
    }
    
    How would this be actually implemented?
  • Have some DB containing the valid zip codes
  • Should the DB call be here?
    • DB is on a different server
    • Complex connection code. Network call. Caching. Access control.
    • Nopes. Single responsibility.
    • Have a different class for all DB operations
  • Same DB will also have details about cities, states, ...

Singleton Pattern

public class ResourceInitializer {
    // where do you initialize all the things?
    public ResourceInitializer() {
        // connect to DB
        // query
        // parse results
        // store data in this.attribute
    }
}
  • What happens if we create a new DB instance for each call?
    • repeated expensive connections
    • DB will get DOS'd
  • Don't want clients to create multiple isntances
  • First instance itself will load all the needed data and store it in attributes
  • Clients must share one common isntance
  • Hide the constructor - private
public class ResourceInitializer {
    private static ResourceInitializer INSTANCE;
    
    private ResourceInitializer() {
        // connect to DB
        // query
        // parse results
        // store data in this.attribute
    }
    public static ResourceInitializer getInstance() {
        if(INSTANCE == null) {
            INSTANCE = new ResourceInitializer();
        }
        return INSTANCE;
    }
}
  • now, clients can't access contructor.
  • they must call getInstance to get an instance
  • same instance shared across all clients
  • now it can have attributes that can be initalized in constructor
public Set<String> zips;

private ResourceInitializer() {
    // init zips
}

ZipValidator {
    bool validateZip(String zip) {
        return ResourceInitializer.getInstance().zips.contains(zip);
    }
}
  • this code saves lot of resources and time