Skip to content

Latest commit

 

History

History
376 lines (320 loc) · 9.9 KB

lld1.md

File metadata and controls

376 lines (320 loc) · 9.9 KB
class Point2D {
    int x;
    int y;
}

class Shape2D {
    String name;
    Point2D location;

    void caculateArea(){
        //some logic
    }
}

class Sqaure extends Shape2D {
    @Override
    void caculateArea(){
        //This will call Shape2D's implmentation
        super.caculateArea();
    }
}

class Client {
    void foo() {
        Square sq = new Square();
        //this will call the square's implementation
        sq.caculateArea();
    }
}

class Beak{
    Shape shape;
    Color color;
    Hardness hardness;
}

class Wing {
    int featherCount;
    int wingSpan;
    Color color;
}

//classes are concepts with attributes, behaviours, releationships
class Bird {
    //attributes
    String color;
    float weight;
    int age;
    String name;
  
    //releationships modeled in a code via attributes
    //A bird has a beak
    Beak beak;
    Wing leftWing, rightWing;
    Leg leftLeg, rightReg;
    //behaviours
    void fly(){
        print("flap wings");
    }
    void eat(){
        print("peck at the ground");
    }
}

class Sparrow extends Bird {
    //every sparrow object is also a bird object as well
    //whatever a bird can do, a sparrow can also do
    //extra attributes
    String favoriteGrains, favoriteWorm 
    //extra relationships
    Tail tail;
    //extra behaviours
    void chirp(){

    }

}

class Client {
    void foo(){
        Bird b = new Bird();
        b.eat();
        b.name = "My Little Bird";
    }
}

//schema enforcement can be provided by using abstract classes or interfaces.
abstract class Bird {
    abstract void fly(); //no implementation
}

class Sparrow extends Bird {
    void fly(){
        //providing implementation
    }
}

abstract class BlueBird extends Bird {
    //since this class is not providing implmementation of Bird,
    //hence marked as abstract
}

class client{
    void foo(){
        Bird b = new Bird(); // you can't use like this. This is an error
        BlueBird b = new BlueBird(); //This is also an error
        Sparrow s = new Sparrow(); //This is fine
        //This doesn't mean that abstract class can't have objects. abstract classes can't be instantiated but it can certainly have objects
        //Here, I am instantiating an sparrow but creating a Bird object.
        Bird b = new Sparrow(); //This is fine
    }
}

//Some birds can't fly
//in this case schema enforcement is a bad design
class Kiwi extends Bird {
    void fly(){
        throw new CantFlyException("I can't fly, I have no wings");
    }
}

//Basically, it says that you should not enforce your children/clients to implement those methods which they don't want to implement
//There are two ways to avoid this.
//1) via class hierarchy
//But, this kind of approach will create 2^n sub classes as for different attributes, you will be maintaining different sub classes
//This problem is also known as combinatorial explosions
class Bird {

}

//This is fine an abstract simple class can extend concrete class
abstract class FlyingBird extends Bird {
    abstract void fly();
}


class Sparrow extends FlyingBird {
    void fly(){
        print("I can fly");
    }
}

class Kiwi extends Bird {
    //No need to implement fly over here
}

//2) another way of solving this is via interfaces
class Bird(){}
    interface Flight {void fly();}
    interface Swim {void swim();}
    interface Run {void run();}
    interface Peck {void peck();} 
    interface HasWing {void getWings();};

//so you are allowed to extend one class but implement multiple interfaces
class Sparrow extends Bird implements Flight, Peck, HasWings {
    void fly(){}
    void peck(){}
    void getWings(){}
}

//java or C# doesn't allow multiple inheritance as this will lead to diamond problem.
//Polymorphism is divided into two categories compile time and runtime. 
//compile time --> method overloading or operator overloading
//runtime --> virtual/overriding 

class/interface  | Abstraction --> Hiding details
inheritance | Generalization (is - a relationship) --> Reusable code / one thing serves multiple purposes
access Modifiers | Encapsulation --> To wrap with something also data hiding
attributes | Association (has - a relationship) --> Connecting multiple concepts/objects together
           | Aggregation - uses relationship
           | Composition - uses relationship but also owns relationship

Generalization and Composition are sub types of association

class Student {
    List<Course> coursesEnrolledIn; //aggregation -->student uses the course but doesn't own it
}

class Course {
    List<Lecture> lectures; //composition --> a lecture can't exist if course doesn't exist
    List<Student> studentsEnrolled; //aggregation --> course uses the student but doesn't own it
}

class Lecture{
    Course course; //aggregation 
}

//Abstraction:- Hiding the inner implementation detaills. Exposing only what the client is going to use
class Car {
    Tire leftFrontTire;
    Window window1;
    void start();
    void honk();
    //using access modifier to enforce abstract
    private void InternalDetails();
}
//Generalization:- Come with a concept of vehicle
interface IVehicle{
    void start();
    void honk();
}

class car implements Vehicle {

}

Hiding details - abstraction
    - hiding the data (encapsulation) --> means abstraction can be achived by using encapsulation
    - hiding the details into a separate concept/class
Hiding data - encapsulation

class Rectangle {
    private int height;
    private int width;

    int area(){
        return height*width;
    }

    //setters
    void setWidth(int newWidth){
        width = newWidth;
    }
    void setHeight(int newHeight){
        height = newHeight;
    }
    //getters
    int getWidth(){
        return width;
    }
    int getHeighth(){
        return height;
    }
}

class Square extends Rectangle{
    @Override
     void setWidth(int newWidth){
        width = newWidth;
    }
    @Override
    void setHeight(int newHeight){
        //if(newHeight!=width){
          //  throw new InvalidStateException("Height and width must be the same");
        //}
       width = height = newHeight;
    }
}

class Client {
    void foo(){
        Rectangle r = new Rectangle();
        r.setWidth(10);
        r.setHeight(20);
        print(r.area());
    }
}

//getters and setters by default present in java world to make the code backward compatible.
//let's say initially its public and client is direcly using that, tmrw I may have validations 
//on that property and make that field private. In that case, the earlier code will break.
//Hence getters and setters are defacto choice

//SOLID
//S-> SRP --> This is straight forward
//O-> Open for extension and closed for modification
class Rectangle{

}
//If the client wants to add new functionality to the rectangle or they want to provide variations of the rectangle then they should not
//have to modify the rectangle class. Client should be able to extend the functonality by simply using inheritance.

//L-> Liskov substituition principle
//Everything which a parent class can do a child class must be able to do. Runtime polymorphism already enforces that like
Bird b = new Sparrow(); //--> It means whatever actvity a bird can do an sparrow can also do as this sparrow is a bird.
//but people can do stupid things in runtime polymorphism rt
class Bird{
    void fly(){
        print("Flap Wings");
    }
}

class Kiwi extends Bird{
    @override 
    void fly(){
        throw new Exception("No wings, i am extinct");
    }
}

class client {
    void foo(){
        //Bird b = new Bird();//working one
        Bird b = new Kiwi();
        b.fly(); // this will throw exception
    }
}

//LSP says that runtime ploymorphism allows you to do such things but don't do such things. Basically it enforces correctness of the system

//I-> Interface segragation principle
//And the way to solve the above problem is via ISP. ISP says that your iterface should be minimal. Means your your client/subclass should not be forced to implement the functionality which they don't want to support.
class Bird {
    void getBorn();
    void die();
}
interface Flying{
    void fly();
}
class Sparrow extends Bird implements Flying{

}

class Kiwi extends Bird {

}
//Therefore, the way to achieve LSP is via ISP
//D-> Dependency Inversion Principle
//High level code should depend on other high level code and not low level details. Don't create your dependencies yourself. Have your client provide that to you.
class Car {}
class ConsumerCar extends Car {
    //these are dependencies
    ConsumerEngine engine;
    RubberWheel leftWheel;
    GlassWindow window;
    Horn horn;
    void honk(){
        this.horn.honk();
    }
}
class CombatCar extends Car {
    ArmoredEngine engine;
    BulletProofSelfInflatingWheel wheel;
    TaintedBulletProofWindow window;
}
//so, it shouldn't be the case like for these exclusive types as mentioned above, we should be creating different classes. Rather than that, I would prefer
//client should inject th dependency what they want actually rather than relying on low level nitty gritties. Like
class Engine{}
class ConsumerEngine extends Engine {}
class ArmoredEngine extends Engine {}

//high level class
class Car {
    Engine engine;
    Wheel wheel;
    Window window;
    Horn horn;
    //here in ctor, we can use Interface as well
    public Car(Engine engine, Wheel wheel, ...){
        this.engine = engine;
        ....
    }
}

//like wise
class client {
    void foo {
        Car combatCar = new Car(new ArmoredEngine(), new BulletProofSelfInflatingWheel());
    }
}
//so, DIP says that high level code should depend on other high level not on low level details. DIP will be achieved by using abstraction on the 
//dependencies and by using dependency Injection

//Inversion of Control
//You are giving up control.

//classes can have state
//Interfaces can't have any state
//classes can have encapsulation
//interfaces by default are public
//abstract classes --> schema enforcement + some implementation
//interfaces --> schema enforcement

//use interfaces as much as you can.