[6/5 TIL] JAVA(객체지향 특징, UML, SOLID, 디자인 패턴)
🍃프로그래머스 백엔드 데브코스 4기 교육과정을 듣고 정리한 글입니다.🍃
객체지향 4가지 특성
캡슐화
- 정보 은닉
- 캡슐화는 객체 내부의 상세한 구현을 외부로부터 감춥니다.
- 접근 제어
- 캡슐화는 객체의 데이터에 private 접근 지정자를 선언해서 외부에서 직접적인 접근을 막고, 메서드를 통해 간접적인 접근을 허용합니다.
- 모듈화
- 캡슐화는 관련된 데이터와 기능을 하나의 모듈로 묶어줍니다.
상속
- 재사용성
- 상위 클래스에서 정의된 필드와 메서드를 하위 클래스가 상속받아 사용할 수 있습니다.
- 다형성
- 상위 클래스에서 정의된 메서드를 하위 클래스에서 오버라이딩하여 다양한 구현을 할 수 있습니다.
- 상속은 공통된 기능을 여러 객체에게 전달할 때 맺는것이 아닌, 추상과 구체 관계에서 맺어야 합니다.
- 원자 > 물질 > 생물 > 동물 > 포유류 > 사람 > 남자 > 유명한
추상화
- 공통 속성과 동작 정의
- 추상화를 통해 객체들 간에 공통적인 속성과 동작을 정의할 수 있습니다.
- 추상 클래스
- 구현체를 갖지 않고, 정의만 갖고있는 것을 abstract method라고 하고, 이러한 메서드를 하나라도 갖고 있는 클래스를 abstract class라 합니다.
- 객체를 직접 생성할 수 없으며, 하위 클래스에서 상속받아 사용합니다.
- 구체적인 기능을 갖고 있으며, 하위 클래스에게 공동퇸 동작이나 상태를 제공합니다.
- 다중 상속이 불가능합니다.
- 인터페이스
- 내부에 변수와 구현된 메서드가 없고, 암묵적으로 모든 메서드가 public이며 추상 메서드로 간주됩니다.
- 구체적인 구현이 없는 메서드만을 제공하며, 클래스들 간에 일관성 있는 동작을 보장합니다.
- 다중 상속이 가능합니다.
다형성
- 메서드 오버로딩
- 같은 이름의 메서드를 매개변수의 개수나 타입에 따라 다르게 정의합니다.
- 메서드 오버라이딩
- 상속 관계에 있는 클래스에서 상위 클래스의 메서드를 하위 클래스에서 재정의합니다.
- 인터페이스 다형성
- 인터페이스를 구현한 여러 클래스의 인스턴스를 해당 인터페이스 타입으로 선언하고 사용할 수 있습니다.
UML(Class Diagram)
그림 출처 바로가기
- 클래스 다이어그램은 객체지향 프로그래밍에서 클래스들의 구조와 관계를 시각적으로 표현하는 도구입니다.
- 클래스의 구조는 클래스명, 필드(속성), 메서드를 표기해서 나타냅니다.
- 이곳을 활용할 수 있습니다.
SOLID 5가지 원칙
객체지향 프로그래밍(OOP)을 잘하기 위한 5가지 원칙입니다.
단일 책임 원칙(Single Responsibility Principle, SRP)
- 클래스는 단 하나의 책임만 가져야 합니다.
- 클래스는 변경되는 이유가 단 하나여야 합니다.
개방-폐쇄 원칙(Open-Closed Principle, OCP)
- 확장에는 열려있고, 수정에는 닫혀 있어야 합니다.
- 기존의 코드를 수정하지 않고도 새로운 동작을 추가할 수 있어야 합니다.
리스코프 치환 원칙(Liskov Substitution Principle, LSP)
- 자식 클래스는 언제나 부모 클래스를 대체할 수 있어야 합니다.
- 상속 관계에서 상위 타입이 사용되는 부분에 하위 타입이 들어가도 아무 문제가 없어야 합니다.
인터페이스 분리 원칙(Interface Segregation Principle, ISP)
- 클라이언트는 자신이 사용하지 않는 인터페이스에 의존해서는 안 됩니다.
- 인터페이스는 클라이언트의 요구에 딱 맞는 형태로 분리되어야 합니다.
의존성 역전 원칙(Dependency Inversion Principle, DIP)
- 구현체가 아니라 추상체에 의존해야 합니다.
- 의존성 주입(DI)과 인터페이스를 활용하여 실현할 수 있습니다.
디자인 패턴
SOLID 원칙을 따라 설계했을 때 발생하는 23가지 공통점(패턴) 입니다.
생성 패턴(Creational Patterns)
팩토리 메서드 패턴(Factory Method Pattern)
- 객체 생성을 처리하는 인터페이스를 정의하고, 서브클래스에서 실제 객체의 생성을 담당하는 패턴입니다.
- 클라이언트는 인터페이스를 통해 객체를 생성하며, 구체적인 객체 생성은 서브클래스에서 이루어집니다.
- 클라이언트는 구체적인 클래스에 대한 의존성을 줄일 수 있습니다.
추상 팩토리 패턴(Abstract Factory Pattern)
- 여러 관련 객체들의 집합을 생성하기 위한 인터페이스를 제공하는 패턴입니다.
- 인터페이스를 통해 관련 객체들을 생성하며, 클라이언트는 구체적인 객체의 클래스를 알 필요가 없습니다.
싱글톤 패턴(Singleton Pattern)
- 단 하나의 인스턴스를 생성하고, 어디서든 접근할 수 있는 전역적인 특성을 제공하는 패턴입니다.
- 클래스의 인스턴스화를 제한하고, 하나의 인스턴스를 공유함으로써 자원 낭비를 줄일 수 있습니다.
- 주로 설정 파일, 로그 처리, 캐시 등에서 사용합니다.
빌더 패턴(Builder Pattern)
- 복잡한 객체의 생성 과정을 단계별로 나누어 처리하는 패턴입니다.
- 생성자에 매개변수가 많을 때 유용하며, 객체 생성 과정을 유연하게 변경할 수 있습니다.
- 빌더 패턴은 객체의 속성을 설정하고, 마지막에 실제 객체를 반환하는 방식으로 동작합니다.
프로토타입 패턴(Prototype Pattern)
- 객체를 복제하여 생성하는 패턴입니다.
- 원본 객체를 기반으로 새로운 객체를 생성하며, 복잡한 초기화 과정을 피할 수 있습니다.
- 프로토타입 패턴은 객체의 타입을 동적으로 결정할 수 있습니다.
구조 패턴(Structural Patterns)
어댑터 패턴(Adapter Pattern)
- 호환되지 않는 인터페이스를 가진 클래스들을 함께 동작하도록 만들기 위한 패턴입니다.
- 어댑터 클래스를 통해 인터페이스를 변환하여 서로 다른 클래스들이 함께 작동할 수 있게 합니다.
브리지 패턴(Bridge Pattern)
- 추상화와 구현을 분리하여 강한 결합을 피하고, 두개의 계층을 독립적으로 확장할 수 있는 패턴입니다.
컴포지트 패턴(Composite Pattern)
- 객체들을 트리 구조로 구성하여 전체와 부분을 나타내는 패턴입니다.
- 개별 객체와 복합 객체를 동일한 방식으로 다룰 수 있도록 합니다.
- 클라이언트는 개별 객체와 복합 객체를 구분하지 않고 사용할 수 있으며, 객체들 간의 계층 구조를 유지합니다.
데코레이터 패턴(Decorator Pattern)
- 기존 객체의 기능을 확장하기 위해 런타임에 동적으로 추가하는 패턴입니다.
- 데코레이터 클래스를 사용하여 객체를 감싸고, 추가적인 기능을 제공합니다.
퍼사드 패턴(Facade Pattern)
- 복잡한 서브시스템을 단순한 인터페이스로 감싸고, 클라이언트가 간단하게 사용할 수 있도록 하는 패턴입니다.
- 퍼사드 클래스는 서브시스템을 대신해서 복잡한 작업을 처리하고, 클라이언트에게 단순한 인터페이스를 제공합니다.
- 클라이언트는 복잡한 서브시스템의 내부 동작을 알 필요 없이 간단하게 사용할 수 있습니다.
프록시 패턴(Proxy Pattern)
- 실제 객체에 대한 Proxy(대리자)를 제공하여 객체에 대한 접근을 제어하고, 추가적인 기능을 제공하는 패턴입니다.
- 프록시 객체는 실제 객체에 대한 접근을 제어하고, 보안, 캐싱 등의 기능을 수행합니다.
- 객체의 생성 및 로딩 시간을 절약할 수 있습니다.
플라이웨이트 패턴(Flyweight Pattern)
- 많은 수의 유사한 객체를 효율적으로 공유하여 메모리 사용량을 줄이는 패턴입니다.
- 공유 가능한 객체를 캐시하여 재사용하고, 객체의 상태를 외부에서 관리합니다.
행위 패턴(Behavioral Patterns)
전략 패턴(Strategy Pattern)
- 알고리즘을 정의하고 캡술화하여 런타임에 알고리즘을 교환할 수 있도록 하는 패턴입니다.
- 동일한 문제를 해결하기 위해 다양한 알고리즘을 제공하고 선택하는 유연성을 제공합니다.
- 인터페이스를 통해 다양한 알고리즘을 구현하고, 클라이언트 객체에서 전략 객체를 하나 선택하여 실행합니다.
템플릿 메서드 패턴(Template Method Pattern)
- 알고리즘의 구조를 정의하는 추상 클래스를 사용하여 알고리즘의 일부 단계를 하위 클래스에 위임하는 패턴입니다.
- 추상 클래스는 템플릿 메서드라 불리는 메서드를 정의하고, 이 메서드 내에서 알고리즘의 공통 로직을 구현합니다.
- 일부 단계는 추상 메서드로 선언되어 하위 클래스에서 구체적으로 구현됩니다.
- 이를 통해 알고리즘의 구조는 고정되지만 일부 단계는 다르게 구현될 수 있습니다.
옵저버 패턴(Observer Pattern)
- 객체 사이의 일대다 종속성을 정의하여 한 객체의 상태 변경이 다른 객체에게 자동으로 통지되도록 하는 패턴입니다.
- 주제 객체와 옵저버 객체로 구성됩니다.
- 주제 객체는 상태를 가지며, 옵저버 객체는 주제 객체의 상태 변화를 감지하고 이에 대응하는 동작을 합니다.
인터프리터 패턴(Interpreter Pattern)
- 주어진 언어의 문법을 해석하고 실행하기 위한 인터프리터를 구현하는 패턴입니다.
- 문법적 요소들을 클래스로 표현하고, 문장을 분석하고 해석하는 인터프리터 클래스를 구현합니다.
- 특정 언어로 작성된 문장을 실행 가능한 형태로 변환하고 동작을 수행합니다.
커맨드 패턴(Command Pattern)
- 요청을 객체로 캡슐화하여 요청을 서로 다른 객체에게 전달하고 실행하는 방법을 제공하는 패턴입니다.
- 요청을 호출하는 객체(Invoker), 요청을 수신하는 객체(Receiver), 요청을 캡슐화하는 객체(Command)로 구성됩니다.
- 커맨드 객체는 호출된 요청을 수행하기 위해 리시버 객체의 메서드를 호출한다.
반복자 패턴(Iterator Pattern)
- 컬렉션 객체의 내부 구조와 상관없이 요소에 순차적으로 접근할 수 있는 방법을 제공하는 패턴입니다.
- 컬렉션 객체와 이를 순회하는 반복자 객체로 구성됩니다.
- 반복자 객체는 현재 요소에 대한 참조를 유지하고, 다음 요소로 이동하거나 컬렉션 끝에 도달했는지 확인하는 기능을 제공합니다.
- 객체 간의 상호작용을 캡슐화하여 객체들이 직접 통신하지 않고 중재자를 통해 통신하도록 하는 패턴입니다.
- 객체는 중재자에게 메시지를 전송하고, 중재자는 이를 적절한 수신자에게 전달합니다.
- 객체들 사이의 직접적인 의존성을 피하고, 메시지 수진 및 처리 역할을 중재자가 담당합니다.
메멘토 패턴(Memento Pattern)
- 객체의 상태를 저장하고 복원하는 메커니즘을 제공하여 이전 상태로의 롤백을 지원하는 패턴입니다.
- 객체의 상태를 메멘토 객체로 저장하고, 필요한 시점에서 해당 메멘토를 사용하여 객체를 이전 상태로 복원합니다.
- 객체의 상태를 관리하고, 상태 변경을 되돌리거나 재사용할 수 있습니다.
상태 패턴(State Pattern)
- 객체의 내부 상태에 따라 행위를 변경할 수 있도록 하는 패턴입니다.
- 상태별로 서로 다른 메서드를 구현하고, 상태 전이를 위한 인터페이스를 제공합니다.
- 상태에 따라 객체의 행위를 동적으로 변경해서 다른 동작을 수행할 수 있습니다.
책임 연쇄 패턴(Chain of Responsibility Pattern)
- 요청을 처리할 수 있는 객체의 연결된 체인을 생성하여 요청을 처리할 객체를 동적으로 결정하는 패턴입니다.
- 요청은 체인 상의 객체들을 순차적으로 탐색하며, 각 객체는 요청을 처리하거나 다음 객체로 전달 가능합니다.
- 요청의 발송자와 수신자의 결합도를 낮추고, 요청 처리의 유연성을 제공합니다.
방문자 패턴(Visitor Pattern)
- 객체 구조를 수정하지 않고 객체들에 대해 새로운 작업을 추가할 수 있는 패턴입니다.
- 객체들은 방문자의 인터페이스를 통해 작업을 수행하며, 객체 구조에 영향을 주지 않습니다.
- 모든 요소들을 방문자가 순회하며 작업을 수행하고, 다양한 방문자를 추가하여 객체들이 다른 작업을 수행할 수 있도록 합니다.