HJW's IT Blog

[Codeit] 6월 1주차 Study 본문

카테고리 없음

[Codeit] 6월 1주차 Study

kiki1875 2024. 6. 23. 15:13

객체 지향 프로그래밍이란?

객체간의 상호작용을 중심으로 하는 프로그래밍

개념적 대상을 하나로 묶는것 → 객체

객체 안에는 무엇이 들어 있는가?

  • 객체의 상태를 나타내는 변수 (유저의 아이디, 생일 등)
  • 객체의 행동을 나타내는 함수 (좋아요, 상품 구매 등)

객체지향의 반대 = 절차 지향 프로그래밍

객체지향을 사용하면 보다 채계적인 프로그래밍이 가능해진다

객체지향의 주요 개념들은 다음과 같다

  • Object (객체)
  • Constructor Function (생성자 함수)
  • Prototype (프로토타입)
  • Class (클래스)
  • Inheritance (상속)
  • Encapsulation (캡슐화)
  • Abstracton (추상화)

객체 만들기

Object-Literal

const user = {
	email: 'asdf@gmail.com',
	bdate: '1992-03-21';,
	buy(item){
		console.log(`${this.email} buys ${item.name}`);
	},
};
  • 위와 같이 중괄호를 쓰고 그 내부에 프로퍼티와 메소드를 나열하는 것을 object-literal 이라 부른다
  • 키 - 값 쌍을 정의하여 생성하게 된다
  • 객체의 간결함, 즉시 초기화, 유연성이 특징이다

Factory-Function

  • 함수를 반환하는 객체
  • new 키워드를 사용하지 않아도 되기 때문에 간단하며 직관적이다
  • 비공개 속성과 메서드를 만들 수 있다. 이는 외부에서 객체의 속성을 직접 조작하지 못하게 하여 encapsulation 을 구현하는데에 유용하다.
const user1 = {
	email: 'asdf@gmail.com',
	bdate: '1992-03-21';,
	buy(item){
		console.log(`${this.email} buys ${item.name}`);
	},
};
const user2 = {
	email: 'asdf@gmail.com',
	bdate: '1992-03-21';,
	buy(item){
		console.log(`${this.email} buys ${item.name}`);
	},
};

  • 위와 같은 방식은 너무 비효율적이며 코드의 양이 많아진다
function createUser(email, bdate){
	const user = {
		email: email,
		bdate: bdate,
		buy(item){
			console.log(`${this.email} buys ${item.name}`);
		},
	};
	return user;
}

const user1 = createUser('kevinheo0413@gmail.com', '1999-04-13');
const user2 = createUser('asdf@gmail.com','1999-05-11');

//아래는 encapsulation의 예제이다

function createPerson(name, age) {
    let privateVariable = 'This is private';

    function privateMethod() {
        console.log(privateVariable);
    }

    return {
        name: name,
        age: age,
        greet() {
            console.log('Hello, ' + this.name);
        },
        showPrivate() {
            privateMethod();
        }
    };
}

const john = createPerson('John', 30);
john.greet(); // Hello, John
john.showPrivate(); // This is private
  • 함수 내부에서 전달받은 파라미터를 통해 객체를 만들고 해당 객체를 반환한다.
  • 이런 형태를 factory function 이라 부른다

Constructor-Function

  • 생성자 함수
function User(email, bdate){
	this.email = email;
	this.bdate = bdate;
	this.buy = function (item) {
		console.log(...);
	}
}

const user1 = new User('chris123@google.com', '1999-01-01');
  • 일반적인 함수와 달리 객체를 생성할 수 있는 함수이다
  • Constructor Function 의 원리
    • 함수 내부에 this 키워드가 생성되는 객체를 가르키게 된다
    • new 가 필수
    • 함수의 이름 중 첫 번째 알파벳을 보통 대문자로 할당한다

Constructor Factory

new 키워드 필요 new 없이 생성 가능
생성된 객체는 생성자 함수의 prototype 을 상속받는다, 별도의 prototype 을 가지지 않으며, 객체의 메서드는 각 인스턴스마다 개별적
encapsulation 을 직접 구현하기 어려움 클로저를 활용하여 비공개 속성과 메서드 구현이 용

Class

ES6 부터 지원

class User {
	constructor(email, bdate){
		this.email = email;
		this.bdate = bdate;
	}
	buy(item){
		console.log(item);
	}
}
  • 프로퍼티와 메소드의 분리
  • new 키워드 필수

<aside> 💡 객체 지향 프로그래밍 언어들은 클래스 기반, 프로토타입 기반으로 나뉜다.

</aside>

객체지향 프로그래밍의 4개의 기둥

추상화, 캡슐화, 상속, 다형성을 일컫는 말.

추상화

어떠한 구체적인 존재를 원하는 방향으로 간략화 하여 나타내는 것을 의미

클래스 설계와 같은 행위도 추상화 과정에 해당

추상화의 주의점 : 프로퍼티와 메소드의 이름을 잘 지어야 한다

캡슐화

개체의 특정 프로퍼티에 직접 접근하지 못하도록 막는 행위

class User {
	constructor(email, bdate){
		this.email = email;
		this.bdate = bdate;
	}
	buy(item){
		console.log(`${this.email} buys ${item.name}`);
	}
	
	get email(){
		return this._email;
	}
	
	set email(address){
		if(address.inludes('@')){
			this._email = address;
		}else{
			throw new Error('invalid email');
		}
	}
}

const user1 = new User('kevinheo0413@gmail.com', '1999-04-13');
user1.email = 'asdf@gmail.com' //setter 메소드 실행
console.log(user1.email); // getter 메소드 실행
  • 위 의 set email 함수는 유저가 설정하려 할때마다 실행된다
  • email 프로퍼티에 값이 설정되는것이 아닌 함수가 실행
  • 이것을 setter 메소드라 한다
  • 숨기고자 하는 프로퍼티의 이름 앞에 _
  • 이상한 값이 email에 할당되는 것을 방지
  • getter 메서드는 _email 이 아닌 email로 호출할 수 있게 해준다
  • console.log(user1.email); 를 하였을 때 email 값이 바로 읽혀지는 것이 아닌 getter 를 통해 호출된다
  • 하지만 위 방법은 완벽한 캡슐화가 아니다. 여전히 _email 변수를 통해 접근할 수 있기 때문인데, 이를 방지하기 위해 closure 를 사용한다
function createUser(email, birthdate) {
  let _email = email;

  const user = {
    birthdate,

    get email() {
      return _email;
    },

    set email(address) {
      if (address.includes('@')) {
        _email = address;
      } else {
        throw new Error('invalid email address');
      }
    },
  };

  return user;
}

const user1 = createUser('chris123@google.com', '19920321');
console.log(user1.email);

  • user 객체 내부에는 _email 이라는 프로퍼티가 없다.
  • 하지만 그렇다고 해서 바깥의 _email변수에 접근할 수도 없기 때문에 _email 에 접근이 불가능 하다
  • 함수 또한 캡슐화가 가능한데, 예제는 다음과 같다
function createUser(email, birthdate) {
  const _email = email;
  let _point = 0;

  function increasePoint() {
    _point += 1;
  }

  const user = {
    birthdate,

    get email() {
      return _email;
    },

    get point() {
      return _point;
    },

    buy(item) {
      console.log(`${this.email} buys ${item.name}`);
      increasePoint();
    },
  };

  return user;
}

const item = {
  name: '스웨터',
  price: 30000,
};

const user1 = createUser('chris123@google.com', '19920321');
user1.buy(item);
user1.buy(item);
user1.buy(item);
console.log(user1.point);

  • buy 함수 내부에서, 외부에 있는 increasePoint()를 실행시키는데, 이 함수 또한 user 객체를 통해 호출할 수는 없다

상속

  • 하나의 객체가 다른 객체의 프로퍼티와 메소드를 물려받는 경우, 이를 상속이라 한다
class User {
	constructor(email, bdate){
		this.email = email;
		this.bdate = bdate;
	}
	buy(item){
		console.log(`${this.email} buys ${item.name}`);
	}
}

class PremiumUser extends User {
	constructor(email, bdate, level){
		this.level = level;
	}
	streamMusicForFree(){
		console.log("free music");
	}
}
  • extends 문법을 통해 상속받는다
  • 상속의 베이스가 되는 클래스를 부모 클래스, 상속받은 클래스는 자식 클래스라 한다
  • 겹치지 않는 부분만 작성하면 되어, 코드가 간결해진다

super

  • 위 코드의 결과는 에러가 나오는데, 이는 파생된 자식 클래스는 super constuctor 가 필요하기 때문이다
  • 자식 클래스의 constructor 내에서 super(email,bdate) 를 선언해주어야 한다
  • 이 과정은 부모 클래스의 생성자 함수를 호출하는 것이다.
  • super → 자식 클래스에서 부모 클래스의 요소를 호출할 때 사

다형성

  • 많은 형태를 가지고 있는 성질 → 하나의 변수가 여러 종류의 객체를 가리킬 수 있는 성
  • 만약 위 코드의 PremiumUser 에서 buy 메소드는 5% 할인된 가격을 결제한다고 가정하면..
  • PremiumUser 내부에 동일한 이름의 메서드를 작성, 내용을 다르게 한다 → 이 과정을 overriding 이라 한다.
  • 만약 여러 user 과 premium user 로 구성된 객체의 배열이 있다고 가정하고 배열을 순차적으로 도는 상황을 가정해보면
users.forEach((user) =>{
	user.buy(item);
});
  • 각 속한 class 에 따른 buy 가 실행된다
  • 이렇게 다양한 종류의 객체를 가르킬 수 있는 것을 다형성 이라 한다
  • 만약 부모 클래스의 메소드가 필요하다면?
    • 자식 클래스의 매소드 내부에 super.메소드명()

instanceof 연산자

  • 정확히 어느 클래스로 생성한 객체인지를 확인하고 싶을때
  • 자식 클래스의 경우, 부모 클래스로 만든 객체로 인정된다

Static

  • 클래스에 직접적으로 딸려있는 프로퍼티와 메소드
  • 객체가 아닌 클래스 자체만으로 접근하여 사용
class Math{
	static PI = 3.14;
	static getCircleRadius(radius){
		return Math.PI * radius * radius;
	}
}

console.log(Math.PI);
console.log(Math.getCircleArea(5));