Hola
SOLID, test integrales, test unitarios, test end to end.
Que significa esto ?
SOLID al rescate
Desarrollo Inicial
Mantenimiento
Costo
Ecuación SOLID
intuitivo + testeable + tolera cambios = SOLID
Singleton
Tight Coupling
Untestability
Premature Optimización
Indescriptive
Duplication
SOLID
Single Responsability
Open/Closed
Liskov substitution
Interface segregation
Dependencie Inversion
No es mi intención herir sentimientos
STUPID
class Singleton {
constructor(){
if(Singleton.instance){
return Singleton.instance;
}
this.title = "my singleton";
Singleton.instance = this;
}
}
let mySingleton = new Singleton()
let mySingleton2 = new Singleton()
console.log("Singleton 1: ", mySingleton.title);
mySingleton.title = "modified in instance 1"
console.log("Singleton 2: ", mySingleton2.title);"Si ni si quiera hemos empezado a escalar la montaña porque preguntarnos que haremos en la cima"
No significa que debamos escribir código poco optimo
//bad
const fruit = ['manzana', 'mango', 'fresa'];
//regular
const fruitList = ['manzana', 'mango', 'fresa'];
//good
const fruits = ['manzana', 'mango', 'fresa'];
//better
const fruitsNames = ['manzana', 'mango', 'fresa'];
//bad
const open = true;
const write = true;
const fruit = true;
//better
const isOpen = true;
const canWrite = true;
const hasFruit = true;
//worse
const reportData = {
name: "Software Crafters",
createdAt: new Date(),
purchases: 100,
conversionRate: 10,
}
function withOutDRY(){
function showReport(reportData) {
const reportFormatted = `
Name: ${reportData.name}
Created at: ${reportData.createdAt}
Purchases: ${reportData.purchases}
Conversion Rate: ${reportData.conversionRate}%
`
console.log("Showing report", reportFormatted)
}
function saveReport(reportData) {
const reportFormatted = `
Name: ${reportData.name}
Created at: ${reportData.createdAt}
Purchases: ${reportData.purchases}
Conversion Rate: ${reportData.conversionRate}%
`
console.log("Saving report...", reportFormatted)
}
showReport(reportData)
saveReport(reportData)
}//better
const reportData = {
name: "Software Crafters",
createdAt: new Date(),
purchases: 100,
conversionRate: 10,
}
function withDRY(){
function formatReport(reportData){
return `
Name: ${reportData.name}
Created at: ${reportData.createdAt}
Purchases: ${reportData.purchases}
Conversion Rate: ${reportData.conversionRate}%
`
}
function showReport(reportData) {
console.log("Showing report...", formatReport(reportData));
}
function saveReport(reportData) {
console.log("Saving report...", formatReport(reportData));
}
showReport(reportData)
saveReport(reportData)
}Ok, vale se lo que estoy haciendo mal.
Pero hmmmm.
Cómo puedo organizar mis funciones y estructuras de datos en componentes ?
y cómo los conecto?
Pero ojo tener una única responsabilidad
no implica hacer una única cosa
class UseCase{
doSomethingWithTaxes(){
console.log("Do something related with taxes ...")
}
saveChangesInDatabase(){
console.log("Saving in database ...")
}
sendEmail(){
console.log("Sending email ...")
}
}class UseCase{
constructor(repo, notifier){
this.repo = repo;
this.notifier = notifier;
}
doSomethingWithTaxes(){
console.log("Do something related with taxes ...")
}
saveChanges(){
this.repo.update();
}
notify(){
this.notifier.notify("Hi!")
}
}
class Repository{
add(){
console.log("Adding in database");
}
update(){
console.log("Updating in database...");
}
remove(){
console.log("Deleting from database ...");
}
find(){
console.log("Finding from database ...");
}
}
class NotificationService{
notify(message){
console.log("Sending message ...");
console.log(message);
}
}
var axios = require('axios');
class TodoExternalService{
requestTodoItems(callback){
const url = 'https://jsonplaceholder.typicode.com/todos/';
axios
.get(url)
.then(callback)
}
}
new TodoExternalService().requestTodoItems(response => console.log(response.data))//infraestructure/ClientWrapper
const axios = require('axios');
export class ClientWrapper{
makeGetRequest(url, callback){
return axios.get(url).then(callback);
}
}
//domain/TodoService
export class TodoService{
client;
constructor(client){
this.client = client;
}
requestTodoItems(callback){
const url = 'https://jsonplaceholder.typicode.com/todos/';
this.client.makeGetRequest(url, callback)
}
}
//idex
import {ClientWrapper} from './infrastructure/ClientWrapper'
import {TodoService} from './domain/TodoService'
const start = () => {
const client = new ClientWrapper();
const todoService = new TodoService(client);
todoService.requestTodoItems(response => console.log(response.data))
}
start();//infraestructure/ClientWrapper
export class ClientWrapper{
makeGetRequest(url, callback){
return fetch(url)
.then(reponse => response.json())
.then(callback)
}
}
//domain/TodoService
export class TodoService{
client;
constructor(client){
this.client = client;
}
requestTodoItems(callback){
const url = 'https://jsonplaceholder.typicode.com/todos/';
this.client.makeGetRequest(url, callback)
}
}
//idex
import {ClientWrapper} from './infrastructure/ClientWrapper'
import {TodoService} from './domain/TodoService'
const start = () => {
const client = new ClientWrapper();
const todoService = new TodoService(client);
todoService.requestTodoItems(response => console.log(response.data))
}
start();Si una clase A es extendida por una clase B, debemos ser capaces de sutituir cualquier instancia de A por cualquier objeto de B sin que el sistema deje de funcionar o se presenten comportamientos inesperados
class Rectangle {
constructor() {
this.width = 0;
this.height = 0;
}
setWidth(width) {
this.width = width;
}
setHeight(height) {
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
class Square extends Rectangle {
setWidth(width) {
this.width = width;
this.height = width;
}
setHeight(height) {
this.width = height;
this.height = height;
}
}
test('Should be able to calculate the area for the rectangle', ()=>{
let rectangle = new Rectangle()
rectangle.setHeight(5)
rectangle.setWidth(4)
expect(rectangle.getArea()).toBe(20)
})
test('Should be able to calculate the area for the square', ()=>{
let square = new Square()
square.setHeight(5)
square.setWidth(4)
expect(square.getArea()).toBe(20)
})
class Figure{
constructor() {
this.width = 0;
this.height = 0;
}
getArea() {
return this.width * this.height;
}
}
class Rectangle extends Figure {
constructor(width, height) {
super();
this.width = width;
this.height = height;
}
}
class Square extends Figure {
constructor(length) {
super();
this.width = length;
this.height = length;
}
}
test('Should be able to calculate the area for the rectangle', ()=>{
let rectangle = new Rectangle(5, 4)
expect(rectangle.getArea()).toBe(20)
})
test('Should be able to calculate the area for the square', ()=>{
let square = new Square(5)
expect(square.getArea()).toBe(25)
})interface Car{
accelerate: () => void;
brake: () => void;
startEngine: () => void;
}
class Mustang implements Car {
accelerate(){
console.log("Speeding up...")
}
brake(){
console.log("Stoping...")
}
startEngine(){
console.log("Starting engine...")
}
}interface Car{
accelerate: () => void;
brake: () => void;
startEngine: () => void;
autoPilot: () => void;
}
class ModelS implements Car {
accelerate(){
console.log("Speeding up...")
}
brake(){
console.log("Stoping...")
}
startEngine(){
console.log("Starting engine...")
}
autoPilot(){
console.log("Self drinving");
}
}
class Mustang implements Car {
accelerate(){
console.log("Speeding up...")
}
brake(){
console.log("Stoping...")
}
startEngine(){
console.log("Starting engine...")
}
autoPilot(){
throw new Error("UnSupported operation");
}
}interface Car{
accelerate: () => void;
brake: () => void;
startEngine: () => void;
}
interface Tesla{
autoPilot: () => void;
}
class ModelS implements Car, Tesla {
accelerate(){
console.log("Speeding up...")
}
brake(){
console.log("Stoping...")
}
startEngine(){
console.log("Starting engine...")
}
autoPilot(){
console.log("Self drinving");
}
}
class Mustang implements Car {
accelerate(){
console.log("Speeding up...")
}
brake(){
console.log("Stoping...")
}
startEngine(){
console.log("Starting engine...")
}
}// hidden dependency
class UseCase{
constructor(){
this.externalService = new ExternalService();
}
doSomething(){
this.externalService.doExternalTask();
}
}
class ExternalService{
doExternalTask(){
console.log("Doing task...")
}
}
// dependency injection
class UseCase{
constructor(externalService: ExternalService){
this.externalService = externalService;
}
doSomething(){
this.externalService.doExternalTask();
}
}
class ExternalService{
doExternalTask(){
console.log("Doing task...")
}
}interface IExternalService{
doExternalTask: () => void;
}
class UseCase {
externalService: IExternalService;
constructor(externalService: IExternalService){
this.externalService = externalService;
}
doSomething(){
this.externalService.doExternalTask();
}
}
class ExternalService implements IExternalService {
doExternalTask(){
console.log("Doing external task...")
}
}
const client = new UseCase(new ExternalService());
client.doSomething();"El testing de software puede verificar la presencia de errores pero no la ausencia de ellos" Edsger Dijkstra
test('return zero if recieve one'){
//Arrange
const n = 1;
//Atc
const result = fibonacci(n);
// Assert
expect(result).toBe(0);
}
test('return zero if recieve one', () => {
expect(fibonacci(1)).toBe(0);
})