javascript - 디자인 패턴

김동하·2020년 10월 6일
0

javascript

목록 보기
43/58

디자인 패턴이란?

좋은 코드를 설계하는 것

Obejct Literal

const module = {
    someProp: 'hello',
    someMethod: function() {
        console.log(this.someProp);
    }
};

일반적인 형태인 객체 리터럴

const phone = {
   battery: 0,
    rechargeBattery: function() {
       return this.battery = 100
    }
};
phone.rechargeBattery(); // battery = 100
phone.battery = 1000 // battery = 1000

사용하기 쉽지만 문제는 보안에 취약하다. 외부에서 프로퍼티가 변경 가능하다. 그래서 익명함수나 클로저로 변수를 보호해야 한다.

const phone = (function(){
   let battery = 0;
   return {
     rechargeBattery: function(){
       battery = 100;
     },
     showRemainBattery: fucntion(){
       return battery
     }
   }
})()


const phone = (function(){
  let battery = 0;
  return {
    rechargeBattery: function(){
      battery = 100;
    },
    showRematineBattery: function(){
      return battery
    },
  }
})()

console.log(phone.showRematineBattery()) // 0
console.log(phone.rechargeBattery()) // undefined
console.log(phone.showRematineBattery()) // 100

하지만 이렇게 하면 또 새로운 phone을 만들기 위해서 계속 즉시실행함수를 실행해야 하는 불편함(코드 효율 떨어짐)이 있다. 그랙서 새로운 변수를 만들어 재사용성을 높이장


function phone() {
    let battery = 0;
    return {
        rechargeBattery: function() {
            battery = 100;
        },
        showRemainBattery: function() {
            return battery;
        }
    }
}
const phone1 = phone();
phone1.rechargeBattery()
const phone2 = phone();
console.log(phone1.showRemainBattery()); // 100
console.log(phone2.showRemainBattery()); // 0

하지만 phone()이 글로벌에 선언되니까 아직도 완벽하지 않다 보완하기 위해서 즉시함수로 바꿔주면 됩니다!

Singletons

싱글레톤스는 하나의 클래스에 인스턴스가 하나만 나오게 하는 것이다. 싱글레톤스 패턴은 인스턴스가 하나도 없는 경우에만 인스턴스를 만드는 메소드를 포함하여 클래스를 생성하는 방법으로 적용할 수 있다. 만약 인스턴스가 존재하면 함수는 그 인스턴스의 참조값을 리턴한다. (이해 안 감)

const userModule = (function() {
    const users = []
    let userId = 0

    return {
        create: (username, password) => {
            let user = {
                id: userId,
                username,
                password
            }

            users.push(user)
            userId++

            return user
        },
        
        get: (username) => {
            let targetUser

            user.forEach((user) => {
                if (user.username === username) {
                    targetUser = user
                }
            })
            return targetUser
        }
    }
})()

console.log(userModule.create('dongha', '123'));
console.log(userModule.create('dongha', '123'));
console.log(userModule.create('dongha', '123'));
console.log(userModule.create('kim', '456'));

문제는 dongha가 있는데도 계속 생성된다. 이때 싱글레톤이 등장한다.


const userModule = (function() {
    const userAddress = {}
    let users = []
    let userId = 0

    return {
        create: (username, password) => {
            if (!userAddress.hasOwnProperty(username)) {
                let user = {
                    id: userId,
                    username,
                    password
                }
                userAddress[username] = users.length + ""
                users.push(user)
                userId++

                return user
            } else {
                return users[userAddress[username]]
            }

        },

        get: (username) => {
            return (userAddress[username]) ? users[userAddress[username]] : null
        }
    }
})()

console.log(userModule.create('dongha', '123'));
console.log(userModule.create('dongha', '123'));
console.log(userModule.create('dongha', '123'));
console.log(userModule.create('kim', '456'));

userAddress라는 객체와 hasownproperty로 이미 정보가 있는지 확인한다.

그럼 잘 작동한다.



const mySingleton = (function() {
    let instance;

    function init() {
        function privateMethod() {
            console.log("private")
        }

        let privateVariable = "private too"
        let privateRandomNumber = Math.random();

        return {
            publicmMethod: function() {
                console.log("public")
            },
            publicProperty: "puclic too",
            getRandomeNumber: function() {
                return privateRandomNumber
            }
        }
    }

    return {
        getInstance: function() {
            if (!instance) {
                instance = init()
            }
            return instance
        }
    }
})()

const singleA = mySingleton.getInstance();
const singleB = mySingleton.getInstance();

mySingleton이 리턴하는 객체의 getInstance 메소드는 instance 변수에 아무것도 할당되지 않았을 때는 init 함수를 실행하려 리턴되는 객체를 instance 변수에 할당하고 이미 instance 변수에 객체가 할당되어있으면 인스턴스 변수를 리턴한다.


const HTTPModule = (function() {
    let instance = null

    return {
        create: function() {
            if (!instance) {
                instance = {
                    get: url => {
                        return this.get(url)
                    },
                    post: (url, data) => {
                        return this.post(url, data)
                    }
                };
                return instance
            } else {
                return instance
            }
        }
    }
})()

const module1 = HTTPModule.create();
const module2 = HTTPModule.create();

비동기 상황에서의 싱글레톤 패턴!

Factory Pattern

팩토리 패턴은 객체를 생설할 때 고려되는 방법 중 하나다. 비슷한 객체를 찍어내듯 반복적으로 생성 가능하다. 생성자 함수를 이용한 것이 아니라 일반 함수에서 객체를 반환하는 것을 팩토리 패턴이라한다.


function createCandy() {
    return {
        type: 'candy',
        flavour: 'strawberry',
        sweetness: 9
    };
}

function createChocolate() {
    return {
        type: 'chocolate',
        flavour: 'almonds',
        sweetness: 7
    };
}

function createGiftBox() {
    return {
        type: 'gift box',
        contains: [
            createCandy(),
            createChocolate()
        ]
    };
}
const gift1 = createGiftBox();

create로 시작하는 함수를 호출하면 매번 새로운 인스턴스를 반환한다. 여러 개의 팩토리를 조합하여 다른 팩토리를 만들 수 있다.

function startTeaTime(){
  let time= new Date()
  let hh = time.getHours()
  let mm = time.getMinutes()
  let ss = time.getSeconds()

console.log(`${hh}:${mm}:${ss}, let's have tea!`);
}

function EarlgreyFactory(cnt){
  let tea = {}

  tea.teabags= cnt
  tea.name =  'Earl grey';
  tea.caffeine = 'strong';
tea.milkAdded = true;
tea.drink = function(){
  this.teabags -= 1

console.log(`${this.teabags} tea bags left`);
}
tea.start = startTeaTime;
return tea
}


const blackTea = EarlgreyFactory(10);
blackTea.drink();
blackTea.start()

Mixin

한 객체의 프로퍼티를 다른 객체에 복사해서 사용하는 패턴이다. 개체의 기능을 그대로 보존하면서 다른 객체에 추가할 때 사용한다.



function EarlgreyFactory(cnt) {
    let tea = {}

    tea.teabags = cnt
    tea.name = 'Earl grey';
    tea.caffeine = 'strong';
    tea.milkAdded = true;
    tea.drink = function() {
        this.teabags -= 1

        console.log(`${this.teabags} tea bags left`);
    }

    return tea
}

function sconeFactory(cnt) {
    const scone = {};
    scone.countScones = cnt;
    scone.name = 'scone';
    scone.sweetness = 6;
    scone.eat = function() {
        this.countScones -= 1;
        console.log(`${this.countScones} scones left`);
    }
    return scone;
}


const chocolateScone = sconeFactory(6);
const blackTea = EarlgreyFactory(10);

function startTeaPartyMixin(food) {
    food.start = function() {
        let time = new Date()
        let hh = time.getHours();
        let mm = time.getMinutes();
        let ss = time.getSeconds();

        console.log(`${hh}:${mm}:${ss}, let's have tea!`);

    }
}
startTeaPartyMixin(blackTea);
blackTea.start();

startTeaPartyMixin()에 객체를 넣어 프로퍼티를 공유한다.

Behavior Delegation / Inheritance

Behavior Delegation는 부모 프로토타입에 저장되어있는 변수나 메소드를 위임받아 사용하는 패턴이다.


function Machine() {
    this.battery = 50;
}

Machine.prototype.rechargeBattery = function() {
    let self = this
    setTimeout(function() {
        self.battery = 100;
        console.log(`${self.battery}% charged`);
    }, 1000);
};

Machine.prototype.turnOn = function() {
    let self = this;
    setTimeout(function() {
        self.battery -= 80;
        console.log(`${self.battery}% of battery remaining`);
    }, 1000);
};

function Robot(usage, owner) {
    Machine.call(this);
    this.usage = usage;
    this.owner = owner;
}

Robot.prototype = Object.create(Machine.prototype);
Robot.prototype.constructor = Robot;

Robot.prototype.soldTo = function(newOwner) {
    this.owner = newOwner;
};


function SmartPhone(brand) {
    Machine.call(this);
    this.brand = brand;
}


SmartPhone.prototype = Object.create(Machine.prototype);
SmartPhone.prototype.constructor = SmartPhone;


const myPhone = new SmartPhone('Apple');

console.log(myPhone)

출처 : https://im-developer.tistory.com/141?category=846746

profile
프론트엔드 개발

0개의 댓글