[TIL] 객체 지향 프로그래밍

krkorklo·2022년 8월 20일
0

TIL

목록 보기
6/29
post-thumbnail

객체 지향 프로그래밍

객체지향타입

  • 추상 데이터 타입과 비교되어 객체를 생성 - 객체 단위로 처리

프로퍼티와 메소드

  • Property : 객체의 속성을 나타내는 접근 가능한 이름과 활용 가능한 값을 가지는 특별한 형태
  • Method : 객체가 가지고 있는 동작

클래스

  • 객체를 만들어내기 위한
  • 연관된 변수와 메서드의 집합

오브젝트

  • 구현할 대상
  • 클래스에 선언된 모양 그대로 생성된 실체
  • 클래스의 인스턴스

인스턴스

  • 클래스, 틀을 바탕으로 구현된 구체적인 실체
  • 실체화된 인스턴스는 메모리에 할당
  • 인스턴스는 객체에 속한다
public class Animal {} // Class

public class Main{
	public static void main(String[] args) {
		Animal cat; // Object

		cat = new Animal(); // Instance
	}
}

인스턴스화

  • 클래스 → 인스턴스
  • 범주나 개념으로부터 실재하는 객체를 만드는 과정

클래스 코드 → 구체화, 개별화 → 객체 인스턴스
객체 인스턴스 → 일반화, 추상화 → 클래스 코드

캡슐화

  • 클래스 내부 변수와 메소드를 패키징해서 내부에 감추는 것
  • 접근제어자로 캡슐화, 은닉화를 구현

추상화

  • 단순화하거나 대표성을 띄는 것
  • 단순화한 구조로 표현

상속과 다형성

다형성

  • 하나의 코드가 여러 자료형으로 구현되어 실행되는 것
  • 같은 코드에서 여러 실행 결과가 나올 수 있다
class Animal {
	public void animalMove() {
		System.out.println("동물이 움직입니다");
	}
}

class Human extends Animal {
	public void animalMove() {
		System.out.println("사람이 두발로 걷습니다");
	}
}
class Tiger extends Animal{
	public void animalMove() {
		System.out.println("호랑이가 네발로 뜁니다");
	}
}
class Eagle extends Animal{
	public void animalMove() {
		System.out.println("독수리가 하늘을 납니다");
	}
}

public class AnimalMove {

	public static void main(String[] args) {
		Animal hAnimal = new Human();
		Animal tAnimal = new Tiger();
		Animal eAnimal = new Human();
	
		AnimalMove test = new AnimalMove();
		test.moveAnimal(hAnimal); // 사람이 두발로 걷습니다
		test.moveAnimal(tAnimal); // 호랑이가 네발로 뜁니다
		test.moveAnimal(eAnimal); // 사람이 두발로 걷습니다

	}

	public void moveAnimal(Animal animal) {
		animal.animalMove();
		// 같은 메서드에서 다른 출력
	}
}

→ 동일한 메서드를 오버라이딩해 하나의 코드가 다양한 구현을 가질 수 있음

→ 유사한 클래스가 추가되는 경우 유지보수에 용이하고 if 문이 사라지는 장점

IS-A 관계

  • 상위 클래스가 일반적인 개념 클래스(Animal)이고 하위 클래스가 구체적인 개념 클래스(Dog, Cat)일때

HAS-A 관계

  • 한 클래스가 다른 클래스를 소유한 관계

class

  • 클래스로부터 인스턴스를 상속받는다
  • new 키워드로 인스턴스를 생성할 수 있고 하위 클래스를 만들 수 있다
  • 클래스를 인스턴스를 사용하는 것처럼 사용할 수 없어 인스턴스를 생성한 후 함수를 불러오는 방식으로 사용한다
class Human{
	constructor(age){
	 this.age = age;
	}
	howoldareyou() {
	 console.log(`this man is ${this.age}years old`;
	}
}

class Chulsoo extends Human {
 howoldareyou(){
  super.howoldareyou();
  console.log(`this man called Chulsoo)`;
 }
}

const chulsoo = new Chulsoo(12);
chulsoo.howoldareyou()

//this man is 12years old
//this man called Chulsoo

Prototype

  • 다른 객체로부터 직접 인스턴스가 상속된다
  • 자바스크립트는 객체를 생성할 때 프로토타입 객체를 복사해서 생성 → 프로토타입 객체의 프로퍼티를 사용함으로써 상속이 가능하다
  • Prototype Chain을 형성해서 상속
function Human(age){
  this.age = age;
}

Human.prototype.howoldareyou = function(){
 console.log(`this man is ${this.age}years old`;
}

function Chulsoo(age){
 Human.call(this, age);
}

Chulsoo.prototype = Object.create(Chulsoo.prototype);
Chulsoo.prototype.constructor = Chulsoo;
Chulsoo.prototype.howoldareyou = function(){
 Human.prototype.howoldareyou.call(this);
 console.log(`this man called Chulsoo)`; 
}

const chulsoo = new Chulsoo(12);
chulsoo.howoldareyou()

//this man is 12years old
//this man called Chulsoo

Prototype Object

  • 자신을 통해 만들어질 객체의 원형

함수가 정의될때는

  • 해당 함수에 constructor 자격 부여 → new로 객체 생성 가능
  • 해당 함수의 prototype Object 생성 및 연결
  • Prototype Object는 일반적인 객체와 같다
  • constructor(Prototype Object와 같이 생성된 함수를 가리킴)와 __proto__(Prototype Link - 객체 생성 시 조상이었던 함수의 Prototype Object를 가리킴)를 가지고 있다
    → Person.prototype을 마음대로 참조할 수 있다
  • Kim 객체는 eyes를 가지고 있지 않아 eyes 속성을 찾을때까지 상위 프로토타입을 탐색 → 프로토타입 체인 (상속)
  • Object를 최상위로 전체적인 구조는 다음과 같다

this와 super

this

  • 인스턴스 자신을 가리키는 참조변수

this()

  • 현재 클래스에 정의된 생성자를 부를 때 사용
  • 생성자가 여러개인 경우 인자에 값을 넣어 호출 가능

super

  • 자식 클래스에서 상속받은 부모 클래스의 멤버 변수를 참조할때 사용

super()

  • 자식 클래스가 자신을 생성할때 부모 클래스의 생성자를 불러 초기화할때 사용

SOLID 원칙

모듈화, 캡슐화, 확장용이성 등을 고려한 소프트웨어 구축을 위한 설계

  • S : Single Responsibility Principle (단일 책임 원칙)
  • O : Open-Closed Principle (열린-닫힌 원칙)
  • L : Liskov Substitution Principle (리스코프 치환 원칙)
  • I : Interface Segregation Principle (인터페이스 분리 원칙)
  • D : Dependency Inversion Principle (의존성 역전 원칙)

SRP (단일 책임 원칙)

  • 하나의 module, class, function이 단 하나의 기능을 꼭 책임져야한다는 개념
class Animal {
    constructor(name: string){ }
    getAnimalName() { } // Animal Property 관리
    saveAnimal(a: Animal) { } // DB 관리
}
  • 위 클래스는 Animal 데이터베이스 관리, Animal Property 관리 두 가지 역할을 하므로 SRP 위반!
  • 추후 DB 관리 기능에 영향을 주도록 변경되면 변경사항에 맞춰 Animal Property의 사용을 만드는 클래스는 반드시 수정 필요
class Animal { // Property 다루는 역할
    constructor(name: string){ }
    getAnimalName() { }
}
class AnimalDB { // DB에 정보를 insert라거나 read하는 역할
    getAnimal(a: Animal) { }
    saveAnimal(a: Animal) { }
}

OCP (열림-닫힘 원칙)

  • 소프트웨어 엔티티는 확장을 위해 열려있고, 수정되어서는 안된다
const animals: Array<Animal> = [
    new Animal('lion'),
    new Animal('mouse')
];
function AnimalSound(a: Array<Animal>) {
    for(int i = 0; i <= a.length; i++) {
        if(a[i].name == 'lion')
            return 'roar';
        if(a[i].name == 'mouse')
            return 'squeak';
    }
}
AnimalSound(animals);
  • 새로운 Animal을 추가하면 로직에 변화 → 새로운 Animal에 대해 닫혀있기 때문에 OCP 위반!
  • makeSound라는 메서드를 Animal 클래스에 생성하고 오버라이딩해서 Animal이 추가되더라도 로직에 변화가 없도록 해야한다

LSP (리스코프 교환 원칙)

  • 하위 클래스는 반드시 상위 클래스와 대체 가능해야 한다
function AnimalLegCount(a: Array<Animal>) {
    for(int i = 0; i <= a.length; i++) {
        if(typeof a[i] == Lion)
            return LionLegCount(a[i]);
        if(typeof a[i] == Mouse)
            return MouseLegCount(a[i]);
        if(typeof a[i] == Snake)
            return SnakeLegCount(a[i]);
    }
}

AnimalLegCount(animals);
  • a에 해당하는 타입을 하위 function인 AnimalLegCount에서 모두 알아야하는게 문제!
function AnimalLegCount(a: Array<Animal>) {
    for(let i = 0; i <= a.length; i++) {
        a[i].LegCount();
    }
}
AnimalLegCount(animals);
  • type에 관계없이 count하는 데에만 신경쓰는것이 중요
  • Animal 클래스에서는 LegCount 메서드를 오버라이딩해서 각각의 역할을 수행하도록 수정하는게 중요!

ISP (인터페이스 분리 원칙)

  • 클라이언트의 세분화된 내용과 같은 세분화된 인터페이스를 만들자
interface Shape {
    drawCircle();
    drawSquare();
    drawRectangle();
}
  • 쓰이지 않는 메서드들이 interface에 구현 → 클라이언트에서 필요하지 않은 메서드를 의존하도록 강요하면 안된다
interface Shape {
	draw();
}

interface ICircle {
    drawCircle();
}
interface ISquare {
    drawSquare();
}
interface IRectangle {
    drawRectangle();
}
interface ITriangle {
    drawTriangle();
}
  • 다음과 같이 세분화가 필요하다

DIP (의존성 역전 원칙)

  • 고수준의 모듈은 저수준 모듈에 의존하면 안되고 추상화에 의존해야한다
  • 추상은 세부사항에 의존해서는 안되고 세부사항이 추상에 의존해야한다
class XMLHttpService extends XMLHttpRequestService {}

class Http {
    constructor(private xmlhttpService: XMLHttpService) { }
    get(url: string , options: any) {
        this.xmlhttpService.request(url,'GET');
    }
    post() {
        this.xmlhttpService.request(url,'POST');
    }
    //...
}
  • 코드를 편집하기 위해서는 모든 Http 인스턴스를 고려해야한다
  • 인터페이스를 만들어 사용하는게 좋다

참고자료
https://medium.com/@flqjsl/js-class-와-prototype-의-차이-7dc1d7531ae0
https://medium.com/@bluesh55/javascript-prototype-이해하기-f8e67c286b67
https://burning-camp.tistory.com/78

0개의 댓글