├── 1.single-responsability.ts ├── 2.open-closed.ts ├── 3.liskov-substitution.ts ├── 4.interface-segregation.ts ├── 5.dependency-inversion.ts └── README.md /1.single-responsability.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | //#region Example 1 3 | //BAD - Too coupled 4 | class Products { 5 | products = []; 6 | getAllProducts() { 7 | const connection = new SQLConnection().get("products"); 8 | return connection.data; 9 | } 10 | } 11 | 12 | //BETTER 13 | class Connection { 14 | SQLConnection = new SQLConnection(); 15 | get(table: string) { 16 | return this.SQLConnection.get(table); 17 | } 18 | } 19 | 20 | class Products { 21 | products = []; 22 | constructor(connection: Connection); 23 | getAllProducts() { 24 | this.products = this.connection.get("products").data; 25 | } 26 | } 27 | //#endregion 28 | 29 | //#region Example 2 30 | //Bad 31 | class Task {} 32 | 33 | class TaskManager { 34 | tasks: Task[]; 35 | 36 | constructor() { 37 | this.tasks = []; 38 | } 39 | 40 | addTask(task: Task): void { 41 | this.tasks.push(task); 42 | } 43 | 44 | completeTask(index: number): void { 45 | this.tasks[index].markAsCompleted(); 46 | } 47 | 48 | displayTasks(): void { 49 | // ...logic 50 | } 51 | } 52 | 53 | const taskManage = new TaskManager(); 54 | const task1 = new Task("Completar el informe"); 55 | 56 | taskManager.addTask(task1); 57 | taskManager.displayTasks(); 58 | 59 | taskManager.completeTask(0); 60 | taskManager.displayTasks(); 61 | 62 | //Better 63 | class TaskManager { 64 | private tasks: Task[]; 65 | 66 | constructor() { 67 | this.tasks = []; 68 | } 69 | 70 | addTask(task: Task): void { 71 | this.tasks.push(task); 72 | } 73 | 74 | completeTask(index: number): void { 75 | this.tasks[index].markAsCompleted(); 76 | } 77 | 78 | getTasks(): Task[] { 79 | return this.tasks; 80 | } 81 | } 82 | 83 | class UIManager { 84 | displayTasks(tasks: task[]): void { 85 | // ...logic 86 | } 87 | } 88 | 89 | const taskManager = new TaskManager(); 90 | const uiManager = new UIManager(); 91 | 92 | const task1 = new Task("Completar el informe"); 93 | taskManager.addTask(task1); 94 | 95 | const tasks = taskManager.getTasks(); 96 | uiManager.displayTasks(tasks); 97 | 98 | taskManager.completeTask(0); 99 | uiManager.displayTasks(tasks); 100 | //#endregion 101 | -------------------------------------------------------------------------------- /2.open-closed.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | //#region Example 1 3 | //BAD 4 | class Button { 5 | text: string; 6 | style: string; 7 | 8 | constructor(text: string, style: string) { 9 | this.text = text; 10 | this.style = style; 11 | } 12 | 13 | render(): void { 14 | if (this.style === "primary") { 15 | // Lógica para renderizar un botón de estilo primario 16 | } else if (this.style === "secondary") { 17 | // Lógica para renderizar un botón de estilo secundario 18 | } else { 19 | throw new Error("Estilo de botón no soportado"); 20 | } 21 | } 22 | } 23 | 24 | // Uso de la clase Button 25 | const primaryButton = new Button("Enviar", "primary"); 26 | primaryButton.render(); 27 | 28 | const secondaryButton = new Button("Cancelar", "secondary"); 29 | secondaryButton.render(); 30 | 31 | //BETTER 32 | 33 | interface ButtonStyle { 34 | render(text: string): void; 35 | } 36 | 37 | class PrimaryButtonStyle implements ButtonStyle { 38 | render(text: string): void { 39 | // ...logic 40 | } 41 | } 42 | 43 | class SecondaryButtonStyle implements ButtonStyle { 44 | render(text: string): void { 45 | // ...logic 46 | } 47 | } 48 | 49 | class Button { 50 | text: string; 51 | style: string; 52 | 53 | constructor(text: string, style: ButtonStyle) { 54 | this.text = text; 55 | this.style = style; 56 | } 57 | 58 | render(): void { 59 | this.style.render(this.text); 60 | } 61 | } 62 | 63 | //#endregion 64 | 65 | //#region Example 2 66 | //BAD 67 | class GreetingService { 68 | language: string; 69 | 70 | constructor(language: string) { 71 | this.language = language; 72 | } 73 | 74 | execute(): string { 75 | switch (this.language) { 76 | case "en": { 77 | return "Hello"; 78 | } 79 | 80 | case "es": { 81 | return "Hola"; 82 | } 83 | 84 | case "fr": { 85 | return "Bonjour"; 86 | } 87 | 88 | default: 89 | return ""; 90 | } 91 | } 92 | } 93 | 94 | //BETTER 95 | 96 | interface LanguageProvider { 97 | greet(): string; 98 | } 99 | 100 | class ENLanguageProvider implements LanguageProvider { 101 | greet(): string { 102 | return "Hello"; 103 | } 104 | } 105 | 106 | class FRLanguageProvider implements LanguageProvider { 107 | // Returns a greeting in french 108 | greet(): string { 109 | return "Bonjour"; 110 | } 111 | } 112 | 113 | class ESLanguageProvider implements LanguageProvider { 114 | // Returns a greeting in french 115 | greet(): string { 116 | return "Hola"; 117 | } 118 | } 119 | 120 | class GreetingService { 121 | languageProvider: LanguageProvider; 122 | 123 | constructor(languageProvider: LanguageProvider) { 124 | this.languageProvider = languageProvider; 125 | } 126 | 127 | execute(): string { 128 | return this.languageProvider.greet(); 129 | } 130 | } 131 | 132 | const provider = new ESLanguageProvider(); 133 | const greetingService = new GreetingService(provider); 134 | greetingService.execute(); 135 | //#region 136 | -------------------------------------------------------------------------------- /3.liskov-substitution.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | //BAD 3 | class Rectangle { 4 | constructor(private width: number, private length: number) {} 5 | 6 | public setWidth(width: number) { 7 | this.width = width; 8 | } 9 | 10 | public setLength(length: number) { 11 | this.length = length; 12 | } 13 | 14 | public getArea() { 15 | return this.width * this.length; 16 | } 17 | } 18 | 19 | class Square extends Rectangle { 20 | constructor(side: number) { 21 | super(side, side); 22 | } 23 | 24 | public setWidth(width: number) { 25 | // A square must maintain equal sides 26 | super.setWidth(width); 27 | super.setLength(width); 28 | } 29 | 30 | public setLength(length: number) { 31 | super.setWidth(length); 32 | super.setLength(length); 33 | } 34 | } 35 | 36 | const rect: Square = new Square(10); 37 | rect.setWidth(20); 38 | rect.getArea(); //200 39 | 40 | //BETTER 41 | interface Shape { 42 | getArea: () => number; 43 | } 44 | 45 | class Rectangle { 46 | widht: number; 47 | lenght: number; 48 | } 49 | 50 | class Square implements Shape { 51 | side: number; 52 | constructor(side) { 53 | this.side = side; 54 | } 55 | getArea() { 56 | return side * 2; 57 | } 58 | } 59 | 60 | const rect2 = new Square(10); 61 | rect2.getArea(); 62 | 63 | 64 | -------------------------------------------------------------------------------- /4.interface-segregation.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | //BAD 3 | interface Worker { 4 | eat: () => void; 5 | work: () => void; 6 | sleep: () => void; 7 | } 8 | 9 | class Chef implements Worker { 10 | work() {} 11 | sleep() {} 12 | eat() {} 13 | } 14 | 15 | class Driver implements Worker { 16 | work() {} 17 | sleep() {} 18 | eat() {} 19 | } 20 | 21 | const kevin = new Chef(); 22 | 23 | //BETTER 24 | interface Workable { 25 | work(): void; 26 | } 27 | 28 | interface Eatable { 29 | eat(): void; 30 | } 31 | 32 | interface Sleepable { 33 | sleep(): void; 34 | } 35 | 36 | class Chef implements Workable, Eatable { 37 | work() { 38 | console.log("El chef está cocinando."); 39 | } 40 | 41 | eat() { 42 | console.log("El chef está comiendo."); 43 | } 44 | } 45 | 46 | class Driver implements Workable, Eatable, Sleepable { 47 | work() { 48 | console.log("El conductor está cocinando."); 49 | } 50 | 51 | eat() { 52 | console.log("El conductor está comiendo."); 53 | } 54 | 55 | sleep() { 56 | console.log("El conductor está durmiendo."); 57 | } 58 | } 59 | 60 | const kevin2 = new Chef(); 61 | -------------------------------------------------------------------------------- /5.dependency-inversion.ts: -------------------------------------------------------------------------------- 1 | //@ts-nocheck 2 | //BAD 3 | class LightBulb { 4 | turnOn(): void { 5 | console.log("Bombilla encendida"); 6 | } 7 | 8 | turnOff(): void { 9 | console.log("Bombilla apagada"); 10 | } 11 | } 12 | 13 | class Switch { 14 | private bulb: LightBulb; 15 | 16 | constructor(bulb: LightBulb) { 17 | this.bulb = bulb; 18 | } 19 | 20 | toggle(): void { 21 | if (this.bulb.isOn()) { 22 | this.bulb.turnOff(); 23 | } else { 24 | this.bulb.turnOn(); 25 | } 26 | } 27 | } 28 | 29 | const bulb = new LightBulb(); 30 | const switcher = new Switch(bulb); 31 | 32 | switcher.toggle(); 33 | 34 | //BETTER 35 | 36 | interface Switchable { 37 | turnOn(): void; 38 | turnOff(): void; 39 | } 40 | 41 | class LightBulb implements Switchable { 42 | turnOn(): void { 43 | console.log("Bombilla encendida"); 44 | } 45 | 46 | turnOff(): void { 47 | console.log("Bombilla apagada"); 48 | } 49 | } 50 | 51 | class Switch { 52 | private device: Switchable; 53 | 54 | constructor(device: Switchable) { 55 | this.device = device; 56 | } 57 | 58 | toggle(): void { 59 | if (this.device.isOn()) { 60 | this.device.turnOff(); 61 | } else { 62 | this.bulb.turnOn(); 63 | } 64 | } 65 | } 66 | 67 | const bulb = new LightBulb(); 68 | const switcher = new Switch(bulb); 69 | switcher.toggle(); 70 | 71 | class Fan implements Switchable { 72 | turnOn(): void { 73 | console.log("Ventilador encendida"); 74 | } 75 | 76 | turnOff(): void { 77 | console.log("Ventilador apagada"); 78 | } 79 | } 80 | 81 | const fan = new Fan(); 82 | const switcher = new Switch(fan); 83 | switcher.toggle(); 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SOLID en Typescript 2 | 3 | En este ejemplo exploramos los 5 principios SOLID con ejemplos en Typescript. 4 | --------------------------------------------------------------------------------