Object Oriented Programming
Tags: javascript, programming, oop, beginners
Published: 2022-07-14 01:59:49

**What is it?**
Object-Oriented Programming (OOP) based on my understanding is that it is a way of coding in which you have to group every data and operation into an object/class and it must be properly grouped.

Sounds easy right? But in reality, it should be planned very well because sometimes data and operations are hard to group and could easily be put in the wrong object. This is where the skills of a programmer came into place to create a well-designed object and I will give some basic information about how to achieve it.


First will talk about:
- Cohesion and Coupling
- 4 Pillars of OOP

**Cohesion and Coupling**
We have the concept of cohesion and coupling in the S.O.L.I.D Principle by Robert C. Martin (a.k.a uncle bob).

**Cohesion**
Cohesion is the measure of how well grouped or related your data and operation are, if it is high cohesion means it is grouped well and if it is not then the cohesion is low. ( The goal is to have a high cohesion ).

**Coupling**
Coupling is the measure of dependency between objects. Objects that have a lot of dependency on other objects are high coupling. Coupling is inevitable but we can design them to lessen coupling by using abstraction or interfaces. ( The goal is to have fewer coupled objects ).


**4 Pillars of OOP**
1. Inheritance
2. Encapsulation
3. Abstraction
4. Polymorphism


**Inheritance**
It is when we want to reuse an existing object by extending or composing them. You can create more generic objects and let more specific objects extend those generic objects.

```ts
// generic object
class Tag {
  className: string;
  id: string;
  constructor(className: string, id: string) {
    this.className = className;
    this.id = id;
  }
  display() { ... }
}

// inheritance
class AnchorTag extends Tag {
  href: string;
  constructor(className: string, id: string, href: string) {
    super(className, id);
    this.href = href;
  }
  // override
  display() { ... }
  // adding feature
  onClick() { ... }
}
```



**Composition ( Preferred over inheritance )**
Inheritance is hard to track when the hierarchy of objects becomes big. When this happens you have to go through the entire parents of the objects just to know what fields and operations they had. ( This means also you are tightly coupled to the parent objects ). With composition, you can refer to an object by the interface to reduce coupling.

```ts
// generic object
class Tag {
  className: string;
  id: string;
  constructor(className: string, id: string) {
    this.className = className;
    this.id = id;
  }
  display() { ... }
}

// inheritance
class AnchorTag {
  href: string;
  tag: Tag; // composing the Tag object
  constructor(className: string, id: string, href: string) {
    this.tag = new Tag(className; id); // this can be improved by providing factory method that returns interface to reduce coupling.
    this.href = href;
  }
  // use Tag display method
  display() {
    this.tag.display();
  }
  // adding feature
  onClick() { ... }
}
```


**Encapsulation**
Is about protecting data from assigning invalid states and hiding where data was taken from. In javascript to achieve encapsulation, you have to wrap the object with a function.

```ts
const user = (name) => {
  let nameField = name;
  return {
    setName: (newName) => {
      /* 
       * validate input to protect 
       * invalid state for nameField
       */
      nameField = newName;
    },
    getName: () => {
      /*
       * hide how nameField retrieved
       */
      return nameField;
    }
  }
}

const tom = user("tom");
// tom.nameField = 123897 is now protected
console.log(tom.getName()) // tom
tom.setName('thomas');
console.log(tom.getName()) // thom
```

This can also be achievable with typescript by using the interface and only exposing accessors.

```ts
interface IUser {
  setName(newName: string);
  getName(): string;
}

class User implements IUser {
  name: string
  constructor(name: string) {
    // validation
    this.name = name;
  }
  setName(newName: string) {
    // validation
    this.name = newName;
  }
  getName(): string {
    // hide how it was retrieved
    return this.name;
  }
}

// factory function
const createUser = (name: string): IUser {
  return new User(name);
}

const user = createUser('tom');
// user.name error name is not defined on interface.
user.setName('thom');
user.getName(); // thom
```

**Abstraction**
Is about hiding complexity or irrelevant implementation of our object to the consumer. 80% of our time as programmers read code and the rest is writing new, improving, and fixing bugs. So it is good to hide the implementation to reduce time consumption on reading. Abstraction also reduces coupling because we are not relying on concrete implementation.

```ts
interface IPlayer {
  play();
}

// Abstracted;
class Player implements IPlayer {
  file: IPlayer;
  constructor(f: IPlayer) {
    this.file = f;
  }
  play() { // abstraction of play();
    this.file.play();
  }
}

// concrete class
class Mp3 implements IPlayer {
  play() {
    // do stuffs here.
  }
}

// concrete class
class Mp4 implements IPlayer {
  play() {
    // do stuffs here
  }
}

// factory function
const createFile(file: string): IPlayer {
  switch(file) {
    case 'mp3':
      return new Mp3();
    case 'mp4':
      return new Mp4();
  }
}

// client
const player = new Player(createFile('mp3'));
player.play();
```

Abstraction is not only for objects this can be found everywhere.

No abstraction.
```ts
// consumer
if (15 > 10 && 15 < 20)) {
  console.log('I inside of range');
}
```

With abstraction.
```ts
// Abstraction
const initRange = (value) => (min, max) => value > min && value < max;
const rangeOf = initRange(15);

// Consumer
// Abstracted
if (rangeOf(10, 20)) {
  console.log('I inside of range');
}
```

**Polymorphism**
It can perform multiple implementations by passing objects or information.

```ts
interface Action {
  act();
}
class People {
  action: Action;
  constructor(action: Action) {
    this.action = action;
  }
  setAction(action: Action) {
    this.action = action;
  }
  perform() {
    this.action.act();
  }
}

class Sing implements Action {
  act() {
    console.log('Singing');
  }
}
class Dance implements Action {
  act() {
    console.log('Dancing');
  }
}

const sarah = new People(new Sing())
sarah.act(); // Singing
sarah.setAction(new Dance());
sarah.act(); // Dancing
```

**Learn Design Patterns**
Object-Oriented Programming is sometimes hard to appreciate, but I encourage you to learn and read Design Patterns because it is based on scenarios where you can have more ideas and appreciate how useful each pillar is based on your use case.

**Thank you**
Thank you for finishing reading my blog, I appreciate it, and feel free to contact me with my email to improve my writing I hope you learned a lot ;)

**Follow me**
Follow me on [TWITTER](https://twitter.com/elemental19961) in which I post some short ideas and share what I learn daily.