디자인 패턴이란 프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 '규약' 형태로 만드렁 놓은 것을 의미함.
오직 하나의 인스턴스만 가지는 패턴. 주로 데이터베이스 연결 모듈에 많이 사용됨.
class Singleton {
private static class singleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return singleInstanceHolder.INSTANCE;
}
}
public class HelloWorld {
public static void main(String[] args) {
Singleton a = Singleton.getInstance(); // 새로 할당하지 않고 get 메서드 사용
Singleton b = Singleton.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
if(a == b) {
System.out.pritnln(true);
}
}
}
하나의 인스턴스를 만들어 놓고, 해당 인스턴스를 다른 모듈들이 공유하며 사용하기 때문에 인스턴스 생성 비용이 줄어듦.
의존성이 높아짐. TDD를 할 때 걸림돌이 됨. TDD를 할 때, 단위 테스트를 주로 하는데, 단위 테스트가 서로 독립적이어야 하며 테스트를 어떤 순서로든 실행할 수 있어야 함. 하지만 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로 테스트마다 독립적인 인스턴스 만들기 어려움.
의존성 주입(Dependency Injection)을 통해 해결할 수 있음.
객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴임.
// 객체 생성 부분 추상화
abstract class Coffee {
public abstract int getPrice();
@Override
public String toString() {
return "Hi this coffee is " + this.getPrice();
}
}
// 팩토리 패턴
class CoffeeFactory {
public Coffee getCoffee(String type, int price) {
if("Latte".equalsIgnoreCase(type)) return new Latte(price);
else if("Americano".equalsIgnoreCase(type)) return new Americano(price);
else return new DefaultCoffee();
}
}
// 하위 클래스에서 구체적인 내용 결정
class DefaultCoffee extends Coffee {
private int price;
public DefaultCoffee() {
this.price = -1;
}
@Override
public int getPrice() {
return this.price;
}
}
class Latte extends Coffee {
private int price;
public Latte(int price) {
this.price = price;
}
@Override
public int getPrice() {
return this.price;
}
}
class Americano extends Coffee {
private int price;
public Americano(int price) {
this.price = price;
}
@Override
public int getPrice() {
return this.price;
}
}
객체의 행위를 바꾸고 싶은 경우 '직접' 수정하지 않고 전략이라고 부르는 '캡슐화한 알고리즘'을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴임.
package org.example;
import java.util.ArrayList;
import java.util.List;
public class Main {
interface PaymentStrategy {
public void pay(int amount);
}
static class KAKAOCardStragtegy implements PaymentStrategy {
private String name;
private String cardNumber;
private String cvv;
private String dateOfExpiry;
public KAKAOCardStragtegy(String nm, String ccNum, String cvv, String expiryDate) {
this.name = nm;
this.cardNumber = ccNum;
this.cvv = cvv;
this.dateOfExpiry = expiryDate;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using KAKAOCard.");
}
}
static class LUNACardStrategy implements PaymentStrategy {
private String emailId;
private String password;
public LUNACardStrategy(String email, String pwd) {
this.emailId = email;
this.password = pwd;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using LUNACard.");
}
}
static class Item {
private String name;
private int price;
public Item(String name, int cost) {
this.name = name;
this.price = cost;
}
public String getName() {
return this.name;
}
public int getPrice() {
return price;
}
}
static class ShoppingCart {
List<Item> items;
public ShoppingCart() {
this.items = new ArrayList<>();
}
public void addItem(Item item) {
this.items.add(item);
}
public void removeItem(Item item) {
this.items.remove(item);
}
public int calculateTotal() {
int sum = 0;
for(Item item : items) {
sum += item.getPrice();
}
return sum;
}
public void pay(PaymentStrategy paymentStrategy) {
int amount = calculateTotal();
paymentStrategy.pay(amount);
}
}
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
Item A = new Item("MacBookM3", 100);
Item B = new Item("MacBookM2", 300);
cart.addItem(A);
cart.addItem(B);
cart.pay(new LUNACardStrategy("yeoli@naver.com", "yeolipw"));
cart.pay(new KAKAOCardStragtegy("Park Seong Yeol", "141313515", "123", "01/13"));
}
}
주체가 어떤 객체의 상태 변화를 관찰하여 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴임.
import java.util.ArrayList;
import java.util.list;
interface Subject {
public void register(Observer obj);
public void unregister(Observer obj);
public void notifyObservers();
public Object getUpdate(Observer obj);
}
interface Observer {
public void update();
}
class Topic implements Subject {
private List<Observer> observers;
private String message;
public Topic() {
this.observers = new ArrayList<>();
this.message = "";
}
@Override
public void register(Observer obj) {
if (!observers.contains(obj)) observers.add(obj);
}
@Override
public void unregister(Observer obj) {
observers.remove(obj);
}
@Override
public void notifyObservers() {
this.observers.forEach(Observer::update);
}
@Override
public Object getUpdate(Observer obj) {
return this.message;
}
public void postMessage(String msg) {
System.out.println("Message sended to Topic:" + msg);
this.message = msg;
notifyObservers(); // 여기서 옵저버에게 알려줌
}
}
class TopicSubscriber implements Observer {
private String name;
private Subject topic;
public TopicSubscriber(String name, Subject topic) {
this.name = name;
this.topic = topic;
}
@Override
public void update() {
String msg = (String) topic.getUpdate(this);
System.out.println(name + ":: got message >> " + msg);
}
}
public class HelloWorld {
public static void main(String[] args) {
Topic topic = new Topic();
Observer a = new TopicSubscriber("a", topic);
Observer b = new TopicSubscriber("b", topic);
Observer c = new TopicSubscriber("c", topic);
topic.register(a);
topic.register(b);
topic.register(c);
topic.postMessage("amumu is op champion!!");
}
}
상속(extends)
구현(implements)
프록시 패턴(procy patter)은 대상 객체(subject)에 접근하기 전 그 접근에 대한 흐름을 가로채 대상 객체 앞단의 인터페이스 역할을 하는 디자인 패턴임.
이터레이터 패턴(iterator pattern)은 이터레이터(iterator)를 사용하여 컬렉션(collection)의 요소들에 접근하는 디자인 패턴임.
노출모듈 패턴(revealing module pattern)은 즉시 실행 함수를 통해 private, public 같은 접근 제어자를 만드는 패턴을 말함.
-> 자바스크립트 내용이라 별로 안 중요한듯
MVC 패턴은 모델(Model), 뷰(View), 컨트롤러(Controller)로 이루어진 디자인 패턴임.
모델
모델은 애플리케이션의 데이터인 데이터베이스, 상수, 변수 등을 의미함
뷰
뷰는 inputbox, checkbox, textarea 등 사용자 인터페이스 요소를 나타냄.
컨트롤러
컨트롤러는 하나 이사의 모델과 하나 이상의 뷰를 잇는 다리 역할을 하며 이벤트 등 메인 로직을 담당함.
MVC 패턴 예시
Spring
MVP 패턴은 MVC 패턴으로부터 파생되었으며 MVC에서 C에 해당하는 컨트롤러가 프레젠터(presenter)로 교체된 패턴임. MVC 패턴과의 차이점은 뷰와 프레젠터는 일대일 관계이기 때문에 MVC 패턴보다 더 강한 결합을 지닌 디자인 패턴임.
MVVM 패턴은 MVC의 C의 해당하는 컨트롤러가 뷰모델(View Model)로 바뀐 패턴임. 뷰모델은 뷰를 더 추상화한 계층이며, MVVM 패턴은 MVC 패턴과는 다르게 커맨드와 데이터 바인딩을 가지는 것이 특징임.
프로그래밍 패러다임(programming paradigm)은 프로그래머에게 프로그래밍의 관점을 갖게 해주는 역할을 하는 개발 방법론임.
선언형 프로그래밍(declarative programing)이란 '무엇을' 풀어내는가에 집중하는 패러다임이며, "프로그램은 함수로 이루어진 것이다." 라는 명제가 담겨 있는 패러다임의 일종임. 함수형 프로그래밍(functional programming)은 선언형 패러다임의 일종임.
함수형 프로그래밍(functional programming)
작은 '순수 함수'들을 블록처럼 쌓아 룆ㄱ을 구현하고 '고차 함수'를 통해 재사용성을 높인 프로그래밍 패러다임임.
-> 자바스크립트에서 선호함
순수 함수
출력이 입력에만 의존하는 함수를 의미함.
고차 함수
함수가 함수를 값처럼 매개변수로 받아 로직을 생성할 수 있는 것을 의미함.
객체지향 프로그래밍(OOP, Object-Oriented Programming)은 객체들의 집합으로 프로그램의 상호 작용을 표현하며 데이터를 객체로 취급하여 객체 내부에 선언된 메서드를 활용하는 방식을 의미함. 설계에 많은 시간이 소요되며 처리 속도가 다른 프로그래밍 패러다임에 비해 상대적으로 느림.
추상화(abstraction)
추상화란 복잡한 시스템으로부터 핵심적인 개념 또는 기능을 간추려내는 것을 의미함.
캡슐화(encapsulation)
캡슐화는 객체의 속성과 메서드를 하나로 묶고 일부를 외부에 감추어 은닉하는 것을 의미함.
상속성(inheritance)
상속성은 상위 클래스의 특성을 하위 클래스가 이어받아서 재사용하거나 추가, 확장하는 것을 의미함.
다형성(polymorphism)
다형성은 하나의 메서드나 클래스가 다양한 방법으로 동작하는 것을 의미함.
오버로딩(overlading)
같은 이름을 가진 메서드를 여러개 두는 것을 의미함.
오버라이딩(overriding)
주로 메서드 오버리아딩(method overridig)을 말하며, 상위 클래스로부터 상속받은 메서드를 하위 클래서가 재정의하는 것을 의미함.
단일 책임 원칙(SRP, Responsibility Principle)
모든 클래스는 각각 하나의 책임만 가져야 한다는 원칙임.
개방-페쇄 원칙(OCP, Open Closed Principle)
유지 보수 사항이 생긴다면 코드를 쉽게 확장할 수 있도록 하고 수정할 때는 닫혀 있어야 한다는 원칙임. 즉, 기존의 코드는 잘 변경하지 않으면서 확장은 쉽게 할 수 있어야 함.
리스코프 치환 원칙(LSP, Liskov Substitution Principle)
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 하는 것을 의미함. 즉, 부모 객체에 자식 객체를 넣어도 시스템이 문제없이 돌아가게 만드는 것을 말함.
인터페이스 분리 원칙(ISP, Interface Segregation Principle)
하나의 일반적인 인터페이스보다 구체적인 여러 개의 인터페이스를 만들어야 하는 원칙을 의미함.
의존 역전 원칙(DIP, Dependency Inversion Principle)
자신보다 변하기 쉬운 것에 의존하던 것을 추상화된 인터페이스나 상위 클래스를 두어 변하기 쉬운 것의 변화에 영향받지 않게 하는 원칙을 의미함.
절차형 프로그래밍은 로직이 수행되어야 할 연속적인 계산 과정으로 이루어져 있음. 장점은 코드의 가독성이 좋고, 실행 속도가 빠름. 단점은 모듈화하기 어렵고 유지 보수성이 떨어짐.