티스토리 뷰

객체 지향 프로그래밍

프로그램을 단순히 데이터와 처리 방법으로 나누는 것이 아니라, 프로그램을 수 많은 '객체(Object)'라는 기본 단위로 나누고 이들의 상호작용으로 서술하는 방식을 말합니다.

 

객체란, 하나의 역할을 수행하는 메소드와 변수(데이터)의 묶음이라고 할 수 있습니다.

 

프로그래밍의 방식은 절차적 -> 구조적 -> 객체지향 방식으로 발전해 왔습니다.

 

✔️절차적 프로그래밍

: Input에서 Output으로의 흐름 관점에서 프로그래밍하는 것으로 어떤 논리를 어떤 순서로 적는 것이 중요합니다. 프로그램의 기능이 중심이 되며, 프로그램이 취급하는 데이터는 중요하게 다루지 않습니다.

 

✔️구조적 프로그래밍

: 프로그램을 함수 단위로 나누고 함수들 간의 호출을 통해 구동되도록 하는 프로그래밍 방식입니다. 프로그램을 큰 문제로 보고 이를 해결하기 위해 작은 문제들로 나눠 해결해 나가는 Top-down 방식입니다.

 

✔️객체 지향 프로그래밍

: 작은 문제들을 해결하기 위한 객체를 만들고 이 객체들을 조합하여 큰 문제를 해결해 나가는 Bottom-up 방식의 프로그래밍입니다. 객체는 한번 독립성, 신뢰성 높게 생성해 놓으면 그 후 객체를 새로 생성할 필요없이 재사용이 가능하므로 개발 시간과 비용을 줄일 수 있습니다.

 

따라서, 객체 지향 프로그래밍을 하면 코드의 재사용성이 높고 유지보수가 용이하기 때문에 개발 시간과 비용을 줄일 수 있다는 장점이 있습니다.

 

OOP의 요소

1.  캡슐화 (Encapsulation)

변수와 함수를 하나의 단위로 묶는 것을 의미합니다. 이는 클래스를 통해 구현이 되며, 해당 클래스의 인스턴스를 생성함으로써 클래스 내부의 멤버 변수와 메소드에 접근할 수 있습니다.

 

정보 은닉 (Information Hiding)
캡슐화에서 파생된 개념으로 내부 구현을 감추어 내부 응집력을 높이고 외부로의 노출을 최소화하여 모듈 간의 결합도를 낮춰 유연함과 유지보수성을 높이는 방법을 말합니다.

일반적으로 3가지 접근 제한이 사용됩니다.

✔️public: 클래스 외부에서 사용 가능하도록 노출
✔️protected: 상속 받은 자식 클래스에서만 사용할 수 있도록 노출
✔️private: 클래스 내부에서만 사용 가능하며 외부로 노출되지 않음

2. 추상화 (Abstraction)

여러가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 적용이라는 사전적 의미를 가지고 있으며, OOP에서는 프로그램을 구성하는 객체들이 어떤 필드와 메소드를 갖는지 추출하는 것을 말합니다. 추상화가 잘 된 클래스를 만들어두면 필요한 경우 이 클래스를 재사용할 수 있다는 장점이 있습니다.

 

3. 상속 (Inheritance)

상속은 자식 클래스가 부모 클래스의 특성과 기능을 그대로 물려받는 것을 말합니다. 자식 클래스에서는 부모 클래스에서 물려받은 필드와 메서드 이외에 다른 기능을 추가할 수도 있고 상속 받은 기능을 수정하여 다시 정의(오버라이딩-Overriding)할 수도 있습니다.

 

4. 다형성 (Polymorphism)

하나의 변수 또는 함수가 상황에 따라 다른 의미로 해석될 수 있는 것을 말하며, 하나의 타입에 여러 객체를 대입할 수 있는 성질을 의미합니다. 이런 다형성을 구현하는 방법 중 대표적인 오버라이딩과 오버로딩에 대해 알아보겠습니다.

 

✔️오버라이딩 (Overriding)

상위 클래스의 메소드를 하위 클래스에서 재정의 하는 것을 말합니다. 여기에는 상속의 개념이 들어가게 됩니다.

 

추상 클래스로 Figure 클래스를 생성한 뒤, 하위 클래스에서 오버라이딩 해야할 메소드를 추가해보겠습니다.

 

public abstract class Figure {
    protected int dot;
    protected int area;
    
    public Figure(int dot, int area) {
        this.dot = dot;
        this.area = area;
    }
    
    public abstract void display();  // 하위 클래스에서 오버라이딩 해야 할 추상 메소드
    
    // getter
    public int getDot() {
        return this.dot;
    }
}

 

하위 클래스로 Triangle, Rectangle을 생성하고 각 클래스에 맞게 추상 메소드를 오버라이딩 하였습니다.

 

public class Triangle extends Figure {
    public Triangle(int dot, int area) {
        super(dot, area);
    }
    
    @Override
    public void display() {
        System.out.println("넓이가 %d인 삼각형입니다.", area);
    }
}

public class Rectangle extends Figure {
    public Rectangle(int dot, int area) {
        super(dot, area);
    }
    
    @Override
    public void display() {
        System.out.println("넓이가 %d인 사각형입니다.", area);
    }
}

 

이렇게 생성된 클래스들을 실제 사용할 때 아래와 같이 타입을 추상 클래스 타입으로 하고 실제 생성되는 객체는 추상 클래스를 상속받은 하위 클래스로 사용하면 다른 코드를 수정하지 않고 그때 그때 필요한 객체를 바꿔서 사용할 수 있습니다. 여기에서는 추상 클래스를 예로 사용하였지만 인터페이스를 사용하는 경우도 크게 다르지 않습니다.

 

public static void main(String[] args) {
    Figure figure = new Triangle(3, 10);  // Triangle, Rectangle 등 원하는 객체로 이 부분만 변경하면 됨
    
    for (int i = 0; i < figure.getDot(); i++) {
        figure.display();
    }
}

// 출력
// 넓이가 10인 삼각형입니다.
// 넓이가 10인 삼각형입니다.
// 넓이가 10인 삼각형입니다.

 

다형성을 사용하지 않는다면 아래와 같이 구현하여야 합니다. 중복되는 코드도 많고 지금은 두가지 경우이지만 더 늘어난다고 하면 코드의 양 또한 만만치 않을 것으로 예상됩니다.

 

public static void main(String[] args) {
    int dot = SCANNER.nextInt();
    
    if (3 == dot) {
        Triangle triangle = new Triangle(dot, 10);
        for (int i = 0; i < triangle.getDot(); i++) {
            triangle.display();
        }
    } else if (4 == dot) {
        Rectangle rectangle = new Rectangle(dot, 10);
        for (int i = 0; i < rectangle.getDot(); i++) {
            rectangle.display();
        }
    }
}

 

오버라이드 다형성 방식을 잘 활용한다면, 기능의 확장과 객체의 수정에 유연한 구조를 가져갈 수 있습니다.

 

✔️오버로딩(Overloading)

이름이 같은 메소드를 상황에 따라 여러 개 선언하여 사용할 수 있는 것으로, 메소드 인자(parameter)의 타입이나 수가 다른 경우에 이름이 같은 여러 개의 메소드를 정의할 수 있습니다. 주로 생성자 오버로딩을 통해 다양한 방식으로 객체를 생성할 수 있습니다.

 

public class Employee {
    ...
    
    public Employee() {  // 기본 생성자
    }
    
    public Employee(String email) {  // email로 직원 객체 생성하기
        this.email = email;
    }
    
    public Employee(String email, String empNum) {  // email과 사번으로 직원 객체 생성하기
        this.email = email;
        this.empNum = empNum;
    }
}

 

SOLID 원칙

객체 지향 프로그래밍 방식을 잘 준수하기 위한 원칙으로 SOLID 원칙이 있습니다.

 

1. Single Responsibility Principle (단일 책임 원칙)

클래스는 하나의 기능만 가지며 하나의 책임(목적, 역할)만을 수행하는데 집중해야 한다는 원칙입니다. 여러 가지의 책임을 나눌 때 각 책임간의 결합도는 낮추고 하나의 책임에 대한 응집도는 높여야 합니다.

 

이를 구현하기 위해 각 책임에 따라 클래스를 분리하고 비슷한 책임을 가지고 있다면 하나의 부모 클래스로 추출합니다. 클래스 이름 또한 책임에 맞게 지어야 합니다.

 

2. Open Close Principle (개방 폐쇄 원칙)

소프트웨어의 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확장에는 열려있고 변경에는 닫혀있어야 한다는 원칙입니다. 이는 요구 사항의 변경이나 추가 요청 사항이 발생하는 경우, 기존 구성요소의 변경은 최소화하면서 이를 쉽게 확장하고 재사용할 수 있어야 한다는 것입니다.

 

즉, 컴파일 타임의 의존성을 인터페이스에 고정시키고 런타임에 의존성을 변경하게 함으로써 추가 클래스를 생성하여도 동작은 추가되지만 기존 기능은 수정되지 않음을 의미합니다.

 

변경될 것과 변경되지 않는 것을 엄격하게 분리하여 인터페이스를 정의하고 구체적인 타입 대신 인터페이스에 의존하도록 코드를 작성해야 합니다.

 

3. Liskov Substitution Principle (리스코브 치환 원칙)

서브 타입은 언제나 기반 타입으로 교체될 수 있어야 한다는 원칙입니다. 즉, 기반 타입(부모 class)의 위치에 서브 타입(자식 class)를 넣어도 어떤 이슈가 발생하지 않아야 한다는 것입니다.

 

이 원칙을 지키기 위해 자식 클래스는 부모 클래스를 상속 받을 때, 부모 클래스가 구현하고 있는 원칙을 그대로 따라야 합니다. 이는 부모 클래스에서 이미 정의된 메소드에 대해서는 자식 클래스가 오버라이딩하지 말아야 한다는 것을 의미합니다.

 

4. Interface Segregation Principle (인터페이스 분리 원칙)

한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다는 원칙으로 어떤 클래스가 다른 클래스에 종속될 때 가능한 최소한의 인터페이스만을 사용해야한다는 것입니다. 다시말해 일반적인 하나의 인터페이스보다 구체적인 여러개의 인터페이스를 구현하는 원칙입니다.

 

인터페이스를 분리함으로써 의존성을 약화시켜 리팩토링 및 구조 변경에 용이하게 만들어 줍니다.

 

5. Dependency Inversion Principle (의존성 역전 원칙)

고수준 모듈(클래스)는 저수준 모듈(클래스)에 의존해서는 안되며 둘 다 추상화에 의존해야 하고, 추상화는 세부사항에 의존해서는 안된다는 원칙입니다.

 

추상적인 계층을 만들어 상위 레벨 모듈이 하위 레벨 모듈에 바로 의존하지 않고 이 추상 계층을 의존하게 함으로써 재사용성과 확장성을 높일 수 있습니다.

 

이 원칙을 잘 지키는 것 중 하나가 DI(Dependency Injection)입니다.

 

참고

- SOLID: velog.io/@lsb156/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EA%B0%9C%EB%B0%9C-5%EB%8C%80-%EC%9B%90%EC%B9%99-SOLID

- SOLID: haedallog.tistory.com/147

- OOP 특징: evan-moon.github.io/2019/08/24/what-is-object-oriented-programming/

- 다형성: woowacourse.github.io/javable/post/2020-10-27-polymorphism/

 

'JAVA' 카테고리의 다른 글

[Java Study 09] 예외(Exception) 처리  (0) 2021.04.26
[Java Study 05] 클래스(Class)  (0) 2021.04.24
[JAVA] JUnit 테스트  (0) 2021.04.20
[JAVA] 스택(Stack)과 큐(Queue)  (0) 2021.04.18
[JAVA] 리스트 (List)  (0) 2021.04.15
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함