[이론] 객체지향 프로그래밍 - OOP

조민수·2024년 10월 16일
0

개발 이론

목록 보기
10/12

OOP (Object - Oriented Programming)

객체지향 프로그래밍이란, 컴퓨터 프로그램을 어떤 데이터를 입력받아 순서대로 처리하고 결과를 도출하는 명령어들의 목록으로 보는 시각에서 벗어나,
여러 독립적인 부품들의 조합, 즉 객체들의 유기적인 협력과 결합으로 파악하고자 하는 컴퓨터 프로그래밍의 패러다임을 의미한다.

쉽게 말해, 프로그램을 객체(Object)라는 기본 단위로 구성해 설계하고 구현하는 방법론이다.

현실세계의 사물, 개념을 소프트웨어 객체로 추상화하여, 코드의 재사용성, 유지보수성, 확장성을 높이는데 중점을 둔 방법론이다.


OOP 장점

  1. 코드의 높은 재사용성
  2. 유지보수의 용이성
  3. 새로운 기능이나 요구사항에 따른 쉬운 확장성
  4. 현실의 개념을 그대로 모델링하며 이해하기 쉬운 직관성

OOP 단점

  1. 작은 프로그램에서의 객체지향 설계의 복잡성 증대
  2. 객체 생성, 상호 작용 간의 오버헤드가 야기하는 성능 하락
  3. 학습 및 적용의 어려움

클래스, 객체, 인스턴스

클래스 : Class

  • 객체를 만들기 위한 설계도, 틀
  • 연관되어 있는 변수와 메서드의 집합
  • 교수님께선 이를 붕어빵 틀이라 하셨다.

객체 : Object

  • 소프트웨어 세계에 구현할 대상
  • 클래스에 선언된 모양 그대로 생성된 실체
  • 클래스의 인스턴스라고도 부른다.
  • 모든 인스턴스를 대표하는 포괄적 의미를 갖는다.

인스턴스 : Instance

  • 소프트웨어로 구현된 구체적 실체
  • 객체를 소프트웨어에 실체화해 메모리에 할당한 것

TypeScript에서의 클래스 정의 예시

class Person {
	// 1. 속성 정의
  	name : string;
 	age : number;
  	
  	// 2. 생성자 정의
  	constructor(name: string, age: number) {
    	this.name = name;
      	this.age = age;
    }
  
  	// 3. 메서드 정의
  	greet(): void {
    	console.log('안녕 난 ${this.name}이고, ${this.age}살이야.');
    }
}

TypeScript에서의 객체와 인스턴스

const minsu: Person = new Person("조민수", 26);
const seojin: Person = new Person("이서진", 24);

minsu.greet();
// 안녕 난 조민수이고, 26살이야.

OOP의 4가지 특징

1. 추상화 : Abstraction

  • 사물이나 표상을 어떤 성질, 공통성, 본질에 착안해 추출하여 파악하는 것.
  • 불필요한 세부 사항을 제거하고, 가장 본질적이고 공통된 부분만 추출해 표현
  • 객체의 공통적인 속성 및 기능을 추출해 정의하는 것
  • 객체지향 언어(Java, Python) 등에서는 추상 클래스, 인터페이스 등으로 구현된다.

TypeScript에서의 추상화 - Interface, Type, Abstract Class로 구현

// 1. Interface
interface Animal {
	name: string;
  	age: number;
  	speak(): void;
}

// 2. Type
type Vehicle = {
	brand: string;
  	year: number;
}

// 3. Abstract class
abstact class Shape {
	color: string;
  
  	constructor(color: sring){
    	this.color = color;
    }
  	
  	// 추상 메서드
  	abstract area(): number;
  	
  	// 일반 메서드
  	displayColor(): void {
    	console.log(this.color);
    }
}

// 추상 클래스를 상속받는 구체 클래스
class Circle extends Shape {
	radius: number;
  	...
    // 추상 메서드 구현
    area(): number {
    	return Math.PI * this.radius * this.radius; 
    }
}

2. 상속 : Inheritance

  • 기존 클래스를 재활용해 새로운 클래스를 작성하는 것.
  • 상위 클래스에서 파생된 하위 클래스는 상위 클래스의 속성메서드를 사용할 수 있다.
  • 중복되는 코드를 최소화하고, 공유하는 속성, 기능에 간편한 접근을 가능케한다.

TypeScript는 extends를 활용해 속성과 메서드를 상속한다.

  • 자식 클래스는 super를 통해 부모 클래스의 메서드, 생성자 등을 호출한다.
  • TypeScript는 다중 상속을 지원하지 않는다.

메서드 오버라이딩 : Method Overriding

  • 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것
  • 부모 클래스의 기본 기능을 유지하면서, 자식의 특성에 맞게 수정할 수 있다.
  • 함수(메서드)의 이름이 같다.
    • TypeScript 경우, override를 명시할 수 있다.

3. 다형성 : Polymorphism

  • 어떤 객체의 속성이나 기능이 상황에 따라 여러 형태를 가지는 것.
  • 같은 인터페이스를 통해 다양한 형태의 객체를 다루는 것
  • 코드의 유연성, 확장성을 높인다.
  • 메서드 오버라이딩, 메서드 오버로딩이 대표적인 예시이다

메서드 오버로딩 : Method Overloading

  • 동일한 이름의 메서드를 여러 번 정의해 서로 다른 매개변수 및 리턴 타입을 기반으로 다양한 방식의 메서드를 호출할 수 있게 하는 것.
  • Java의 경우, 전통적인 메서드 오버로딩을 지원하나,
    TypeScript의 경우, 함수 시그니처를 사용한다.

TypeScript의 함수 시그니처

class Calculator {
    // 오버로드 시그니처 정의
    add(a: number, b: number): number;
    add(a: string, b: string): string;
    add(a: number, b: number, c: number): number;

    // 실제 구현부
    add(a: any, b: any, c?: any): any {
        if (typeof a === "number" && typeof b === "number" && typeof c === "number") {
            return a + b + c;
        } else if (typeof a === "number" && typeof b === "number") {
            return a + b;
        } else if (typeof a === "string" && typeof b === "string") {
            return a + b;
        } else {
            throw new Error("잘못된 인자 타입입니다.");
        }
    }
}

4. 캡슐화 : Encapsulation

  • 클래스 안의 서로 연관있는 속성, 기능들을 하나의 캡슐로 만들어 외부로부터 보호하는 것
  • 데이터를 외부로부터 숨기고(hide), 데이터를 조작하는 메서드만 공개(open)하는 것
  • 데이터의 무결성을 보호하고, 객체 간 상호 작용을 명확히한다.
  • 데이터 보호 + 데이터 은닉의 목적성을 가진다.
  • 접근제한자(public, private, protected)를 통해 내부 속성과 메서드에 대한 접근성을 제한할 수 있다.

public

  • 어디서나 접근 가능하며, 생략 가능하다.
class Hello {
	name: string;
  	// 생략가능
  	...
    public greet(): void { ... };
}

private

  • 해당 클래스의 인스턴스에서만 접근 가능하다.
class Hello {
  	private age: number;
	
	...
    private getAge(): number {
    	return this.age;
    }
}

protected

  • 클래스 내부와 자식 클래스에서만 접근 가능하다.
class Hello {
	protected sex: string;
  	
  	...
    protected getSex(): string { ... };
}

readonly

  • 읽기 전용 속성, 정말 수정되면 안되는 값(속성)에 선언한다.
class Hello {
	readonly school: string;
  	...
}

SOLID 5원칙

객체지향 프로그래밍 및 설계의 다섯 가지 기본 원칙

1. S : SRP(Single Responsibility Principle)

  • 단일 책임 원칙
  • 한 클래스는 하나의 책임만 가져야 하며, 그 책임을 완전히 캡슐화해야 한다.
  • 즉, 클래스는 변경이 필요한 이유가 오직 하나여야함

2. O : OCP(Open/Closed Principle)

  • 개방/폐쇄 원칙
  • 확장엔 열려있어야 하고(Open), 변경엔 닫혀있어야 한다. (Closed)
  • 기존 코드를 수정하지 않고도 기능을 추가할 수 있어야 한다.

3. L : LSP(Liskov's Substitution Principle)

  • 리스코프 치환 원칙
  • 자식 클래스는 부모 클래스를 대체할 수 있어야 하며, 부모 클래스에서 기대되는 동작을 보장해야 한다.

4. I : ISP(Interface Segregation Principle)

  • 인터페이스 분리 원칙
  • 클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.
  • 하나의 일반적인 인터페이스보다 여러 개의 구체적인 인터페이스 사용이 바람직하다.

5. D : DIP(Dependency Inversion Principle)

  • 의존 역전 원칙
  • 고수준 모듈은 저수준 모듈에 의존해선 안되며, 둘 다 추상화에 의존해야 한다.
  • 추상화는 세부 사항에 의존해선 안되며, 세부 사항이 추상화에 의존해야 한다.

마치며...

객체지향은 가깝고도 먼, 그런 것이다...
마치 이해했다고 하지만 구현하면서 허점이 가장 많이 발생하는 방법론이기도 하다.

특히, 설계 과정에서의 치밀함, 높은 정확성이 필수적이라 생각하는데,
이는 전체 개발 과정에서의 시간 소요를 필히 증대시키기 때문에 가장 어렵게 생각하고 있다.

[참고자료]

profile
사람을 좋아하는 Front-End 개발자
post-custom-banner

0개의 댓글