## Design Patterns
<br />
<br />
###### Eduardo Lopes Ferreira <br /><i class="fa fa-github"></i>[@eduardo1306](https://www.github.com/eduardo1306)
<br />
---
## Sumário
<br />
######
<span>1. <!-- .element: class="fragment" data-fragment-index="1" -->**S.O.L.I.D**<!-- .element: class="fragment" data-fragment-index="1" --></span><br /> <span>2.<!-- .element: class="fragment" data-fragment-index="2" --> **Creational Design Patterns**<!-- .element: class="fragment" data-fragment-index="2" --></span><br /> <span>3.<!-- .element: class="fragment" data-fragment-index="3" --> **Dependency Injection**<!-- .element: class="fragment" data-fragment-index="3" --></span><br /> <span>4.<!-- .element: class="fragment" data-fragment-index="4" --> **Structural Design Patterns**<!-- .element: class="fragment" data-fragment-index="4" --></span><br />
---
## S.O.L.I.D
###### São príncipios da programação para boas práticas na hora de desenvolver o código e resolver problemas curriqueiros
<br />
<span>- **S**ingle Responsability<!-- .element: class="fragment" data-fragment-index="1" --></span><br /> <span>- **O**pen/Closed<!-- .element: class="fragment" data-fragment-index="2" --></span><br /> <span>- **L**iskov Substitution<!-- .element: class="fragment" data-fragment-index="3" --></span><br /> <span>- **I**nterface Segregation<!-- .element: class="fragment" data-fragment-index="4" --></span><br /> <span>- **D**ependency Inversion<!-- .element: class="fragment" data-fragment-index="5" --></span><br />
---
## Single Responsability Principle
###### Cada classe deve ter apenas uma responsabilidade.

---
## Open/Closed Principle
###### Uma classe deve ser aberta a extensões e fechada para modificações.
<br />
---
## Liskov Substitution Principle
###### Uma subclasse deve performar as mesmas ações de uma classe.
<br />
---
## Interface Segregation Principle
###### As classes devem depender de implementações de interfaces, de forma que não seja generalizada.

---
## Dependency Inversion Principle
###### Uma classe não deve ser conectada diretamente a uma ferramenta e sim a uma interface.
<br />
---
## Creational Design Patterns
###### São Design Patterns que permitem criação de objetos da melhor maneira possível. São eles:
<br />
<span>-<!-- .element: class="fragment" data-fragment-index="1" --> **Singleton**<!-- .element: class="fragment" data-fragment-index="1" --></span><br /> <span>-<!-- .element: class="fragment" data-fragment-index="2" --> **Factory**<!-- .element: class="fragment" data-fragment-index="2" --></span><br /> <span>-<!-- .element: class="fragment" data-fragment-index="3" --> **Loose Coupling**<!-- .element: class="fragment" data-fragment-index="3" --></span><br />
<br />
---
## Singleton
###### Tem como característica uma única instância em toda a aplicação.
```typescript
export class Human {
height: number = 0;
weight: number = 0;
age: number = 0;
constructor() {
if(Human._instance) {
throw new Error ('Cannot initialize singleton class using new!');
}
Human._instance = this;
}
private static _instance: Human = new Human();
public static get instance(): Human {
return Human._instance;
}
}
let eduardo = Human._instance;
```
---
## Factory
###### Permite possibilidades de criar objetos em tempo de execução, isentando de instanciar cada classe.
```typescript
export interface ITransport {
deliver(): void;
}
class Truck implements ITransport {
public deliver(): void {
console.log("Shipping by the road!");
}
}
class Boat implements ITransport {
public deliver(): void {
console.log("Shipping by the sea!");
}
}
export class ShippingCompany {
static getTruck(): Truck {
return new Truck();
}
static getBoat(): Boat {
return new Boat();
}
}
ShippingCompany.getTruck().deliver(); // Shipping by the road;
ShippingCompany.getBoat().deliver(); // Shipping by the sea;
```
---
## Loose Coupling
###### Permite que vários componentes interligados em uma aplicação possam compartilhar/conversar de forma que as classes não se dependam entre elas, apenas usando a implementação da interface.
```typescript=
export interface IPost {
id: number;
title: string;
body: string;
author: string;
}
export interface IPostsServices {
getAll(): Promise<IPost[]>;
save(post: IPost): Promise<void>;
}
export interface IExportPostsServices {
export(post: IPost[]): string;
}
export default class PostsServices implements IPostsServices {
private _posts: IPost[] = [];
constructor() {
this._posts = [
{
id: 4,
title: 'Instagram is awesome',
body: 'Something important',
author: 'Mark Zuckerberg',
},
{
id: 5,
title: 'Netflix is awesome',
body: 'Something important',
author: 'Reed Hastings',
},
]
}
getAll(): Promise<IPost[]> {
return Promise.resolve(this._posts);
}
save(post: IPost): Promise<void> {
this._posts.push(post);
return Promise.resolve();
}
export(service: IExportPostsServices): Promise<string> {
return this.getAll().then(posts => service.export(posts));
}
}
export class MockPostServices implements IPostsServices {
posts: IPost[] = [];
constructor() {
this.posts = [
{
id: 1,
title: 'Facebook is awesome!' ,
body: 'Something important',
author: 'Mark Zuckerberg',
},
{
id: 2,
title: 'Microsoft is awesome!' ,
body: 'Something important',
author: 'Bill Gates',
},
]
}
getAll(): Promise<IPost[]> {
return Promise.resolve(this.posts);
}
save(post: IPost): Promise<void> {
this.posts.push(post);
return Promise.resolve();
}
export(service: IExportPostsServices): Promise<string> {
return this.getAll().then(posts => service.export(posts));
}
}
export class JSONExportServices implements IExportPostsServices {
export(posts: IPost[]): string {
return JSON.stringify(posts);
}
}
let mockService = new MockPostServices()
mockService.export(new JSONExportServices).then(posts => console.log(posts));
```
---
## Dependency Injection
<br /><span>-<!-- .element: class="fragment" data-fragment-index="1" --> **Como/Por que utilizar** <!-- .element: class="fragment" data-fragment-index="1" --></span><br /> <span>-<!-- .element: class="fragment" data-fragment-index="2" --> **Injeção de Dependência e Inversão de Dependência**<!-- .element: class="fragment" data-fragment-index="2" --></span><br /> <span>-<!-- .element: class="fragment" data-fragment-index="3" --> **Containers (Tsyringe)**<!-- .element: class="fragment" data-fragment-index="3" --></span><br />
---
## Como/Por que utilizar
###### Ajuda a evitar o alto nível de acoplamento do código. Facilitando a manutenção/implementação de novas funcionalidades.
<br />
```typescript
export default class Notification {
public notify(): void {
console.log('Notify my friends');
}
}
export default class Post {
constructor(private notification: Notification) {
}
public publish(text: string): void {
console.log(text);
this.notification.notify();
}
}
const post = new Post(new Notification);
post.publish('Design Patterns are Awesome!!'); // Notify my friends
```
---
## Injeção de Dependência e Inversão de Dependência
###### A combinação dos dois é bastante utilizado para abstrairmos o máximo da depêndencia direta.
```typescript
import ICreateAppointmentsDTO from '@modules/appointments/dtos/ICreateAppointmentsDTO';
import Appointment from '../typeorm/entities/Appointments';
export default interface IAppointmentsRepository {
create(data: ICreateAppointmentsDTO): Promise<Appointment>;
findByDate(date: Date): Promise<Appointment | undefined>;
}
@injectable()
class CreateAppointmentService {
constructor(
@inject('AppointmentsRepository')
private appointmentsRepository: IAppointmentsRepository,
) {}
```
---
## Containers (Tsyringe)
###### São gerenciadores e automatizadores de instânciação e injeção de dependência.<br /> O Tsyringe é uma lib desenvolvida pela Microsoft com o intuito de criar containers para utilizarmos a injeção de dependência.
```typescript
import { container } from 'tsyringe';
import '@modules/users/providers';
import './providers';
import IAppointmentsRepository from '@modules/appointments/repositories/IAppointmensRepository';
import AppointmentsRepository from '@modules/appointments/infra/typeorm/repository/AppointmentsRepository';
import IUsersRepository from '@modules/users/repositories/IUserRepository';
import UsersRepository from '@modules/users/infra/typeorm/repositories/UserRepository';
container.registerSingleton<IAppointmentsRepository>(
'AppointmentsRepository',
AppointmentsRepository,
);
container.registerSingleton<IUsersRepository>(
'UsersRepository',
UsersRepository,
);
```
---
## Structural Design Patterns
<br /><span>-<!-- .element: class="fragment" data-fragment-index="1" --> **Decorators** <!-- .element: class="fragment" data-fragment-index="1" --></span><br /> <span>-<!-- .element: class="fragment" data-fragment-index="2" --> **Adapter**<!-- .element: class="fragment" data-fragment-index="2" --></span><br /> <span>-<!-- .element: class="fragment" data-fragment-index="3" --> **Facade**<!-- .element: class="fragment" data-fragment-index="3" --></span><br />
---
## Decorators
###### Permitem adicionar novos comportamentos aos objetos.
```typescript
abstract class Car {
public description: string;
public getDescription(): string {
return this.description;
};
public abstract cost(): number;
}
class Renegade extends Car {
public description = 'Reneage';
public cost(): number {
return 8800;
}
}
class Troller extends Car {
public description = 'Troller 4x4'
public cost(): number {
return 14000;
}
}
abstract class CarOptions extends Car {
decoratedCar: Car;
public abstract getDescription(): string;
public abstract cost(): number;
}
class LeatherSeat extends CarOptions {
constructor(public car: Car){
super();
this.decoratedCar = car;
}
public getDescription(): string {
return this.decoratedCar.getDescription() + ' with leather seat!';
}
public cost(): number {
return this.decoratedCar.cost() + 100;
}
}
let renegade = new Renegade();
let troller = new Troller();
renegade = new LeatherSeat(renegade);
troller = new LeatherSeat(troller);
```
---
## Adapter
###### Permite adaptar objetos com interfaces incompatíveis.
```typescript
interface IPhone {
useLightning(): void;
}
interface Android {
useMicroUSB(): void;
}
class iPhone11 implements IPhone {
useLightning(): void {
console.log('using lightning port...');
}
}
class Samsung implements Android {
useMicroUSB(): void {
console.log('using micro USB...')
}
}
class LightningToMicroUSBAdapter implements Android {
constructor(private iphone: IPhone){}
useMicroUSB(): void {
console.log('Want to use micro USB, converting to lightning...');
this.iphone.useLightning();
}
}
```
---
## Facade
###### Sintetiza o encapsulamento com interface simples estruturando um módulo por completo.

---
## Bibliografias
- https://www.udemy.com/course/design-patterns-in-typescript/learn/lecture/11677766#overview
- https://www.youtube.com/watch?v=WPOLDEk1LF0
- https://medium.com/backticks-tildes/the-s-o-l-i-d-principles-in-pictures-b34ce2f1e898
- https://refactoring.guru/pt-br/design-patterns/catalog
- https://github.com/microsoft/tsyringe
---
{"metaMigratedAt":"2023-06-15T11:07:57.906Z","metaMigratedFrom":"YAML","title":"Design Patterns","breaks":true,"slideOptions":"{\"transition\":\"slide\"}","contributors":"[{\"id\":\"a20e5c4b-eacd-4a91-873f-bbc6816f7cf6\",\"add\":18377,\"del\":6248}]"}