디자인 패턴
- 프로그램 설계시 발생했던 문제들을 하나의 "규약"으로 만들어 놓은 것의 집합체.
♤ 들어가기전, 언어에 대해서!
- 클래스
- 정의 : 객체 지향 프로그래밍에서 데이터를 정의하고, 메서드(동작)을 묶어서 하나의 설계도를 만든 것
- 특징
- 객체를 생성하기 위한 틀 역할.
- 필드(속성)와 메서드(동작)로 구성.
- 예를 들어, Car라는 클래스는 자동차의 속성(브랜드, 색상), 동작(운전, 정지)등 을 정의할 수 있습니다.
- 특징
- 정의 : 객체 지향 프로그래밍에서 데이터를 정의하고, 메서드(동작)을 묶어서 하나의 설계도를 만든 것
public class Car {
String brand;
String color;
public void drive() {
System.out.println("Driving...");
}
}
- 인스턴스
- 정의 : 클래스를 기반으로 메모리에 생성된 실제 객체
- 특징
- 클래스는 설계도이면, 인스턴스는 설계도를 바탕으로 생성된 실체 실체 실체.
- 동일한 클래스로 여러 인스턴스 만들 수 있음
- 특징
- 정의 : 클래스를 기반으로 메모리에 생성된 실제 객체
public class Main {
public static void main(String[] args) {
// Car 클래스의 인스턴스 생성
Car myCar = new Car();
myCar.brand = "Toyota";
myCar.color = "Red";
myCar.drive(); // 출력: Toyota is driving!
}
}
- 모듈
- 정의 : 서로 관련된 코드(클래스, 함수, 변수 등등)의 집합으로 프로그램의 독립적인 단위를 말함.
- 특징
- 큰 프로그램을 여러 부분으로 나누어 개발, 관리, 재사용을 쉽게 함.
- 각 모듈은 독립적으로 개발하고 테스트 할 수 있음.
- 보통 파일 단위로 나뉜다 (java의 경우, .class)
- 특징
- 정의 : 서로 관련된 코드(클래스, 함수, 변수 등등)의 집합으로 프로그램의 독립적인 단위를 말함.
package vehicle;
public class Car {
String brand;
public Car(String brand) {
this.brand = brand;
}
public void drive() {
System.out.println(brand + " is driving!");
}
}
// Main.java
import vehicle.Car;
public class Main {
public static void main(String[] args) {
Car car = new Car("Honda");
car.drive(); // 출력: Honda is driving!
}
}
- TDD(Test Driven Development)
- 정의 : 테스트를 먼저 작성하고, 그 테스트를 통과하는 코드를 작성하는 개발 방법론
- 특징
- 테스트 작성 → 코드 구현 → 리팩토링 순서로 진행
- 코드의 품질과 안정성을 높이는데 도움
- 테스트가 실패하는 상태에서 개발 시작.
- 장점
- 높은 코드 품질
- 빠른 피드백 제공
- 리팩토링 시 버그 방지!!
- 특징
- 정의 : 테스트를 먼저 작성하고, 그 테스트를 통과하는 코드를 작성하는 개발 방법론
// 1. 테스트 작성
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CalculatorTest {
@Test
public void testAdd() {
Calculator calc = new Calculator();
assertEquals(5, calc.add(2, 3)); // 테스트 기대값: 5
}
}
// 2. 테스트를 통과하는 코드 작성
public class Calculator {
public int add(int a, int b) {
return a + b;
}
}
- 단위 테스트
- 정의 : SW의 작은단위(보통 하나의 함수 or 메서드)
- 특징
- 객체를 생성하기 위한 틀 역할.
- 필드(속성)와 메서드(동작)로 구성.
- 예를 들어, Car라는 클래스는 자동차의 속성(브랜드, 색상), 동작(운전, 정지)등 을 정의할 수 있습니다.
- 특징
- 정의 : SW의 작은단위(보통 하나의 함수 or 메서드)
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class StringUtilsTest {
@Test
public void testIsPalindrome() {
StringUtils utils = new StringUtils();
assertTrue(utils.isPalindrome("madam")); // "madam"은 팰린드롬
}
}
class StringUtils {
public boolean isPalindrome(String input) {
String reversed = new StringBuilder(input).reverse().toString();
return input.equals(reversed);
}
}
1. 싱글톤 패턴
- 하나의 클래스에, 하나의 인스턴스만을 가지는 패턴
- 하나의 인스턴스를 다른 모듈에 공유하며 사용
- 인스턴스 생성 비용이 줄어드는 장점, 하지만 의존성이 높아진다는 단점
- 데이터베이스 연결에 많이 사용
♤ 의존성이 높아지면?
- 유연성 저하
- 시스템의 일부를 수정하거나 교체하기가 어려워짐 → 시스템 확장 및 유지보수가 복잡해짐
- A 클래스가 B클래스에 강하게 의존시, B를 변경할때, A도 영향을 받음 ㅠㅠ
- 예시 : A 클래스가 특정 구현 클래스 의존하면, 새로운 구현을 하거나, 기존 구현을 교체하기 위해 A도 수정을 해야합니다 ㅠㅠ
- A 클래스가 B클래스에 강하게 의존시, B를 변경할때, A도 영향을 받음 ㅠㅠ
- 시스템의 일부를 수정하거나 교체하기가 어려워짐 → 시스템 확장 및 유지보수가 복잡해짐
- 테스트 어려움
- 독립적으로 테스트하기가 어려움
- 의존하는 다른 클래스나 외부 서비스가 없으면 테스트하는데 복잡해짐
- A 클래스가 DB와 직접 연결되어 있으면, Unit Test를 실행하기 위해 DB를 또 따로 준비..ㅠ
- 해결방법 : 의존성 주입(Dependency Injection), 인터페이스를 사용해서 의존성을 느슨하게 만들고, Mock(목) 객체 활용하기!
- A 클래스가 DB와 직접 연결되어 있으면, Unit Test를 실행하기 위해 DB를 또 따로 준비..ㅠ
- 의존하는 다른 클래스나 외부 서비스가 없으면 테스트하는데 복잡해짐
- 독립적으로 테스트하기가 어려움
- 재사용성 저하
- 특정 환경, 기술, 구현에 강하게 묶여있어서 독립적으로 재사용하는 것을 어렵게 만듬
- A 클래스가 DB나 library에 종속되어 있으면, 다른 프로젝트에서 재사용 하기위해 의존성을 같이 가져와야함.
- 코드가 특정환경에 고정됨 -> 재사용성이 떨어지고, 프로젝트간 공유가 어려워짐
- A 클래스가 DB나 library에 종속되어 있으면, 다른 프로젝트에서 재사용 하기위해 의존성을 같이 가져와야함.
- 특정 환경, 기술, 구현에 강하게 묶여있어서 독립적으로 재사용하는 것을 어렵게 만듬
- 변경의 전파
- 의존성이 높으면 한 클래싀의 변경이 다른 클래스들로 쉽게 전파됨 → 예상못하는 버그가 생길 수 있음
- B 클래스가 변경되면 A 클래스도 수정해야할 수 있으며, 수정사항이 A를 의존하는 다른 클래스에 영향
- 작은 변경사항이 전체에 파급 효과를 일으킬 가능성이 있어, 유지보수가 어려워짐..
- B 클래스가 변경되면 A 클래스도 수정해야할 수 있으며, 수정사항이 A를 의존하는 다른 클래스에 영향
- 의존성이 높으면 한 클래싀의 변경이 다른 클래스들로 쉽게 전파됨 → 예상못하는 버그가 생길 수 있음
- 결합도 증가
- 클래스 간 결합도가 증가해서, 객체 간 관계를 복잡하게 만듬 → 시스템 복잡도 증가로 인해 관리가 어려워짐...
- 의존성 관리 비용 증가
- 의존성 관리를 위해, 빌드 도구(Maven, Gradle)를 통해 외부 라이브러리를 관리해야함.
- 의존성 출동, 버전 문제 발생
- 의존성 관리를 위해, 빌드 도구(Maven, Gradle)를 통해 외부 라이브러리를 관리해야함.
♤ 해결 방법은?
- 의존성 역전 원칙(DIP)
- 고수준 모듈이 저수준 모듈에 의존하지 않도록 설계한다.
- 인터페이스나 추상 클래스를 통해 의존성을 느슨하게 만든다.
- 의존성 주입 (Dependency Injection)
- 객체 간 의존 관계를 직접 설정하지 않고, 외부에서 주입받도록 설계해서 유연성과 테스트 용이성 높임
- SOLID 원칙 준수 (이 부분은 따로 정리하겠습니다. 아래의 링크를 확인해주세요)
- 모듈화
- 의존성을 명확히 분리하여, 각 모듈이 독립적으로 동작할 수 있도록 한다.
- 테스트를 통한 의존성 관리
- 모의(Mock) 객체를 사용하여 독립적으로 Unit Test를 수행
SOLID 원칙
급하신 분들은 마지막에 요약해놨으니, 그부분만 보셔도 무관합니다! 들어가기에 앞서...♤ 객체 지향 프로그래밍?정의절차적 프로그래밍 : 반복될 가능성이 있는 것들을 재사용이 가능한 함수
ghwjd5684.tistory.com
public class Singleton {
// 1. private static instance 변수를 선언 (유일한 인스턴스)
private static Singleton instance;
// 2. private 생성자를 선언하여 외부에서 인스턴스 생성을 차단
private Singleton() {
System.out.println("Singleton Instance Created");
}
// 3. public static 메서드를 통해 유일한 인스턴스에 접근
public static Singleton getInstance() {
if (instance == null) { // 인스턴스가 생성되지 않았으면 새로 생성
instance = new Singleton();
}
return instance; // 이미 생성된 인스턴스를 반환
}
// 추가 메서드: 싱글톤 클래스에서 제공할 기능
public void showMessage() {
System.out.println("Hello from Singleton!");
}
}
// Main.java (다른 클래스임)
public class Main {
public static void main(String[] args) {
// 싱글톤 인스턴스 가져오기
Singleton singleton1 = Singleton.getInstance();
singleton1.showMessage(); // 출력: Hello from Singleton!
// 두 번째 인스턴스 요청
Singleton singleton2 = Singleton.getInstance();
singleton2.showMessage(); // 출력: Hello from Singleton!
// 동일한 인스턴스인지 확인
System.out.println(singleton1 == singleton2); // 출력: true
}
}
'CS > 디자인 패턴' 카테고리의 다른 글
(CS) 디자인 패턴 - 전략 패턴 (1) | 2025.01.09 |
---|---|
(CS) 디자인 패턴 - 팩토리 패턴 (0) | 2025.01.09 |