Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jalinson: Drop Everything & Learn Jul 31 - Aug 13 #29197

Closed
Tracked by #29427
nollymar opened this issue Jul 11, 2024 · 1 comment
Closed
Tracked by #29427

Jalinson: Drop Everything & Learn Jul 31 - Aug 13 #29197

nollymar opened this issue Jul 11, 2024 · 1 comment

Comments

@nollymar
Copy link
Contributor

No description provided.

@nollymar nollymar changed the title Jalinson: Drop Everything and Learn Jul 10 - Jul 30 Jalinson: Drop Everything & Learn Jul 31 - Aug 13 Aug 1, 2024
@zJaaal
Copy link
Contributor

zJaaal commented Aug 9, 2024

I continued with the same Udemy Course of the last time.

Time Spent: 1h 10min

Today I reinforced the concept of the Builder pattern. Which is a pattern that allows to build objects or structures in a step by step process.

This useful for objects that needs to be built in a procedural way or should have different states along a process or has way too many properties to be initialized in a single call.

I reinforced the 2 ways of using the builder pattern, Simple (Fluent and Not Fluent) and Complex (More builders inside)

Also, had an exercise at the end of the lecture.

Simple:

const TAG_INDENT = 2;
class Tag {
    constructor(name = '', text = '') {
        this.name = name;
        this.text = text;
        this.children = [];
    }

    toStringImpl(indent) {
        let html = [];
        const INDENTATION = ' '.repeat(indent * TAG_INDENT);

        html.push(`${INDENTATION}<${this.name}>\n`);
        if (this.text.length > 0) {
            html.push(' '.repeat(TAG_INDENT * (indent + 1)));
            html.push(this.text);
            html.push('\n');
        }

        for (let child of this.children) html.push(child.toStringImpl(indent + 1));

        html.push(`${INDENTATION}</${this.name}>\n`);
        return html.join('');
    }

    toString() {
        return this.toStringImpl(0);
    }

    static create(name) {
        return new HtmlBuilder(name);
    }
}

class HtmlBuilder {
    constructor(rootName) {
        this.root = new Tag(rootName);
        this.rootName = rootName;
    }

    // Non-fluent interface
    addChild(childName, childText) {
        const child = new Tag(childName, childText);
        this.root.children.push(child);
    }

    addChildFluent(childName, childText) {
        const child = new Tag(childName, childText);
        this.root.children.push(child);
        return this;
    }

    toString() {
        return this.root.toString();
    }

    build() {
        return this.root;
    }

    clear() {
        this.root = new Tag(this.rootName);
    }
}

// We can rewrite the code below using the builder pattern
const hello = 'hello';
let html = [];

// This is construction logic
html.push('<p>');
html.push('hello');
html.push('</p>');
console.log(html.join(''));

const words = ['hello', 'world']; // See how is getting complicated
html = [];
html.push('<ul>\n');

for (let word of words) html.push(`  <li>${word}</li>\n`); // Same construction logic

html.push('</ul>');

console.log(html.join(''));

// New Approach using the builder pattern

let builder = new HtmlBuilder('ul');
for (let word of words) builder.addChild('li', word);

console.log(builder.toString());

// We can initialize from Tag

builder = Tag.create('ul');
for (let word of words) builder.addChild('li', word);

console.log(builder.toString());

builder.clear();

// Fluent interface

builder.addChildFluent('li', 'foo').addChildFluent('li', 'bar').addChildFluent('li', 'baz');

console.log(builder.toString());

Complex:

class Person {
    constructor() {
        this.streetAddress = this.postcode = this.city = '';
        this.companyName = this.position = '';
        this.annualIncome = 0;
    }

    toString() {
        return `Person lives at ${this.streetAddress}, ${this.city}, ${this.postcode}\nand works at ${this.companyName} as ${this.position} earning ${this.annualIncome}`;
    }
}

class PersonBuilder {
    constructor(person = new Person()) {
        this.person = person;
    }

    get lives() {
        return new PersonAddressBuilder(this.person);
    }

    get works() {
        return new PersonJobBuilder(this.person);
    }

    build() {
        return this.person;
    }
}

class PersonJobBuilder extends PersonBuilder {
    constructor(person) {
        super(person);
    }

    at(companyName) {
        this.person.companyName = companyName;
        return this;
    }

    asA(position) {
        this.person.position = position;
        return this;
    }

    earning(annualIncome) {
        this.person.annualIncome = annualIncome;
        return this;
    }
}

class PersonAddressBuilder extends PersonBuilder {
    constructor(person) {
        super(person);
    }

    at(streetAddress) {
        this.person.streetAddress = streetAddress;
        return this;
    }

    withPostcode(postcode) {
        this.person.postcode = postcode;
        return this;
    }

    in(city) {
        this.person.city = city;
        return this;
    }
}

// Usage
const pb = new PersonBuilder();
const person = pb.lives
    .at('123 London Road')
    .in('London')
    .withPostcode('SW12BC')
    .works.at('Fabrikam')
    .asA('Engineer')
    .earning(123000)
    .build();

console.log(person.toString());

Exercise:

// Exercise

class CodeBuilder {
    constructor(className) {
        this.className = className;
        this.fields = [];
    }

    addField(name) {
        this.fields.push(name);
        return this;
    }

    toString() {
        const mappedFields =
            this.fields.map((field) => `    this.${field} = ${field};`).join('\n') + '\n  }\n';

        const constructor = this.fields.length
            ? `  constructor(${this.fields.join(', ')}) {\n${mappedFields}`
            : '';

        const classString = `class ${this.className} {\n${constructor}}`;

        return classString;
    }
}

let cb = new CodeBuilder('Person');
cb.addField('name').addField('age');
console.log(cb.toString());

// Output:
// class Person {
//   constructor(name, age) {
//     this.name = name;
//     this.age = age;
//   }
// }

// Empty class
cb = new CodeBuilder('Foo');
console.log(cb.toString());

// Output:
// class Foo {
// }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
Development

No branches or pull requests

2 participants