자바와 객체지향 - 스프링과 디자인 패턴

YUNU·2023년 7월 6일
0

객체지향

목록 보기
8/9
post-thumbnail

📘 자바와 객체지향


🟦 스프링과 디자인 패턴

🔷 스프링

"자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량급 애플리케이션 프레임워크"

디자인 패턴은 객체 지향의 특성 중 상속(extends), 인터페이스(interface/implements), 합성(객체를 속성으로 사용)을 이용한다.

🔷 Adapter Pattern

Adapter는 변환기라 할 수 있고 변환기는 서로 다른 두 인터페이스 사이에 통신이 가능하도록 해준다.

JDBC가 어댑터 패턴을 이용해 다양한 데이터베이스 시스템을 단일한 인터페이스로 조작할 수 있게 해주는 예이다.

또한 JRE도 마찬가지로 어댑터의 역할을 한다.

Adapter Pattern은 객체를 속성으로 만들어서 참조하는 디자인 패턴
➡️ 호출당하는 쪽의 메서드를 호출하는 쪽의 코드에 대응하도록 중간에 Adapter를 통해 호출

public class A {
	void tellnameA() {
  	// ...
    }
}

public class B {
    void tellnameB() {
    //...
    }
}

비슷한 수행을 하는 메서드를 위와 같이 따로 만들어 사용하지 않고
아래와 같이 메서드명을 통일하여 tellnameA,B 에 대한 변환기를 만든다

public class A {
	void tellname() {
  	// ...
    }
}

public class B {
    void tellname() {
    //...
    }
}

main() {
A a = new A();
B b = new B();

a.tellname();
b.tellname();

}

🔷 Proxy Pattern

제어 흐름을 조정하기 위한 목적으로 중간에 대리자를 두는 패턴 (프록시는 대리자라는 뜻)

OCP(개방 폐쇄 원칙), DIP(의존 역전 원칙)이 적용된 디자인 패턴

  • Proxy는 실제 서비스와 같은 이름의 메서드를 구현한다. 이때 인터페이스를 사용
  • Proxy는 실제 서비스에 대한 참조 변수를 갖는다.
  • Proxy는 실제 서비스의 같은 이름을 가진 메서드를 호출하고 그 값을 클라이언트에 반환한다.
  • Proxy는 실제 서비스의 메서드 호출 전후에 별도의 로직을 수행할 수도 있다.

인터페이스를 사용하면 서비스 객체가 들어갈 자리에 대리자 객체를 투입해 클라이언트 쪽에서는 실제 서비스 객체를 통해 메서드를 호출하고 반환값을 받는지, 대리자 객체를 통해 메서드를 호출하고 반환값을 받는지 알 수 없게 처리할 수 있다.

package 프록시

public interface DoSomething {
	String Something();
}
package 프록시

public class Do implements DoSomething {
	public String Something() {
     return "썸띵";
    }
}
package 프록시

public class Proxy implements DoSomething {
	DoSomething doSomething;
    
    public String Something() {
    	System.out.println("호출에 대한 흐름 제어가 주목적, 반환 결과를 그대로 전달");
        
        doSomething = new Do();
        return doSomething.Something();
    }
}
package 프록시

public class Client {
	public static void main(String[] args) {
      //프록시를 이용한 호출
      doSomething proxy = new Proxy();
      System.out.println(proxy.Something());
    }
}

🔷 Decorator Pattern

메서드 호출의 반환값에 변화를 주기 위해 중간에 장식자를 두는 패턴

구현은 프록시 패턴과 같음
OCP(개방 폐쇄 원칙), DIP(의존 역전 원칙)이 적용된 디자인 패턴

  • Decorator는 실제 서비스와 같은 이름의 메서드를 구현한다. 이때 인터페이스를 사용
  • Decorator는 실제 서비스에 대한 참조 변수를 갖는다.
  • Decorator는 실제 서비스의 같은 이름을 가진 메서드를 호출하고 그 반환값에 장식을 더해 클라이언트에 반환한다.
  • Decorator는 실제 서비스의 메서드 호출 전후에 별도의 로직을 수행할 수도 있다.

프록시 패턴데코레이터 패턴
제어 흐름 변경 혹은 별도의 로직 처리를 목적으로 함
클라이언트가 받는 반환값을 특별한 경우가 아니면 변경하지 않음
클라이언트가 받는 반환값에 장식을 더함
package 데코레이터

public class Decorator implements DoSomething {
	DoSomething doSomething;
    
    public String Something() {
    	System.out.println("호출에 대한 장식 주목적, 반환 결과에 장식을 더해 클라이언트에 전달");
        
        doSomething = new Do();
        return "장식" + doSomething.Something();
    }
}
package 데코레이터

public class Client {
	public static void main(String[] args) {
      //프록시를 이용한 호출
      doSomething decorator = new Decorator();
      System.out.println(decorator.Something());
    }
}

🔷 Singleton Pattern

클래스의 인스턴스(객체)를 하나만 만들어 사용하는 패턴

오직 인스턴스를 하나만 만들고 그것을 계속해서 재사용

싱글턴 패턴을 적용할 경우 의미상 두 개의 객체가 존재해선 안됨
-> 객체 생성을 위한 new에 제약 필요
-> 생성된 단일 객체를 반환할 수 있는 메서드 필요

  • new를 실행할 수 없도록 생성자에 private 접근 제어자 지정
  • 유일한 단일 객체를 반환할 수 있는 정적 메서드 필요
  • 유일한 단일 객체를 참조할 정적 참조 변수 필요
package 싱글턴

public class Singleton {
	static Singleton 정적_참조_변수;
    
    // 생성자에 private 접근 제어자 지정
    private Singleton() {}; 
    
  	//객체를 반환하는 메서드
    public static Singleton getInstance() {
    	//정적참조변수에 객체 할당돼있지 않은 경우에 new를 통해 객체 생성
    	if (정적_참조_변수 ==null) {
        	정적_참조_변수 = new Singleton();
        }
        
        return 정적_참조_변수;
    }
}
main() {

Singleton One = Singleton.getInstance();
Singleton Two = Singleton.getInstance();

}

One, Two는 모두 메모리에서 하나의 단일 객체 :Singleton를 참조한다.

단일 객체의 경우 공유 객체로 사용되기에 속성을 갖지 않는다. -> 다른 참조 변수에 영향 끼칠 수 있기 때문
(읽기 전용 속성 or 다른 단일 객체에 대한 참조를 속성으로 갖는 것은 문제되지 않음)

🔹 싱글턴 패턴 특징

  • private 생성자 가짐
  • 단일 객체 참조 변수를 정적 속성으로 가짐
  • 단일 객체 참조 변수가 참조하는 단일 객체를 반환하는 getInstance() 정적 메서드를 가짐
  • 단일 객체는 쓰기 가능한 속성을 갖지 않는 것이 정석

🔷 Template Method Pattern

상위 클래스에 공통 로직을 수행하는 Tempate method와 하위 클래스에 오버라이딩을 강제하는 Abstract method 또는 선택적으로 오버라이딩 할 수 있는 Hook method를 두는 패턴 (템블릿-견본)
➡️ 상위 클래스의 견본 메서드에서 하위 클래스가 오버라이딩한 메서드를 호출하는 패턴

템플릿 메서드패턴은 DIP(의존 역전 원칙)을 활용

템플릿 메서드공통 로직을 수행
로직 중에 하위 클래스에서 오버라이딩한 추상 메서드/훅 메서드 호출
템플릿 메서드에서 호출하는
추상 메서드
하위 클래스가 반드시 오버라이딩 해야 함

-------------------------------
템플릿 메서드에서 호출하는
훅 메서드
----------------------------------------------------------------------------
하위 클래스가 선택적으로 오버라이딩 함

public abstract class 동물 {
	//Template method
    public void 템플릿메서드() {
    	//...
    }
    
    //Abstract method
    abstract void 추상메서드();
    
    //Hook method
    void 훅메서드() {
    	//...
    }
}

public class 판다 extends 동물 {
	@Override
    //Abstract method 오버라이딩
    void 추상메서드() {
    	//...
    }
    
    @Override
    //Hook method 오버라이딩
    void 훅메서드() {
    	//...
    }
}

🔷 Factory Method Pattern

객체지향에서 팩토리는 객체를 생성한다
➡️ 팩토리 메서드는 객체를 생성하는 메서드
➡️ 팩토리 메서드 패턴은 하위 클래스에서 팩터리 메서드를 오버라이딩해서 객체를 반환하는 것

오버라이드된 메서드가 객체를 반환하는 패턴

DIP(의존 역전 원칙)을 활용

🔷 Strategy Pattern

디자인 패턴의 꽃이라 불리는 전략 패턴

클라이언트가 전략을 생성해 전략을 실행할 컨텍스트에 주입하는 패턴

OCP(개방 폐쇄 원칙), DIP(의존 역전 원칙)이 적용된 패턴

🔹 전략 패턴 구성 요소

  • 전략 메서드를 가진 전략 개체
  • 전략 객체를 사용하는 컨텍스트(전략 객체의 사용자/소비자)
  • 전략 객체를 생성해 컨텍스트에 주입하는 클라이언트(제 3자, 전략 객체의 공급자)

클라이언트는 다양한 전략 중 하나를 선택해 생성한 후 컨텍스트에 주입
1. 클라이언트 ----전략 객체 생성----> 전략
2. 클라이언트 ----전략 객체 주입----> 컨텍스트


예시)
사육사가 음식을 판다에게 급여하면 판다는 제공받은 음식에 따라 식사를 한다.

전략 - 음식 / 컨텍스트 - 판다 / 클라이언트(제 3자) - 사육사

//인터페이스

package 전략패턴

public interface Strategy {
	public abstract void runStrategy();
}

//전략

package 전략패턴

public class Strategy대나무 implements Strategy {
	@Override
    public void runStrategy() {
    	System.out.println("와그작");
    }
}
package 전략패턴

public class Strategy딸기 implement Strategy {
	@Override
    public void runStrategy() {
    	System.out.println("딸기는 안먹어");
    }
}

//컨텍스트

pacakge 전략패턴

public class 판다 {
	void runContext(Strategy strategy) {
    	System.out.println("밥 시간");
        strategy.runStrategy();
        System.out.println("노는 시간");
    }
}

//클라이언트

pacakge 전략패턴

public clss Client {
	public static void main(String[] args) {
    	Strategy strategy = null;
        판다 푸바오 = new 판다();
        
        // 푸바오에게 대나무를 준다.
        strategy = new Strategy대나무();
        푸바오.runContext(strategy);
        
        // 푸바오에게 딸기를 준다.
                strategy = new Strategy딸기();
        푸바오.runContext(strategy);

//실행결과

밥 시간
와그작
노는 시간
밥시간
딸기는 안먹어
노는 시간

템플릿 메서트 패던 - 상속 이용
전략 패턴 - 객체 주입 이용

단일 상속만이 가능한 자바에서는 상속이라는 제한이 있는 템플릿 메서드 패턴보다는 전략 패턴을 많이 활용

🔷 Template Callback Pattern(견본/회신 패턴)

전략 패턴의 변형
스프링의 3대 프로그래밍 모델 중 하나인 DI(의존성 주입)에서 사용하는 특별한 형태의 전략 패턴

전략을 일명 내부 클래스로 구현한 전략 패턴

OCP(개방 폐쇄 원칙), DIP(의존 역전 원칙)이 적용된 패턴

전략 패턴과 모두 동일하나 전략을 익명 내부 클래스로 정의해서 사용하는 차이가 있음

중복되는 부분을 컨텍스트로 이관하여 코드가 간결해짐
➡️ 스프링은 리팩터링된 템플릿 콜백 패턴을 DI에 적극 활용

//인터페이스

package 템플릿콜백패턴

public interface Strategy {
	public abstract void runStrategy();
}

//컨텍스트

pacakge 템플릿콜백패턴

public class 판다 {
	void runContext(Strategy strategy) {
    	System.out.println("밥 시간");
        strategy.runStrategy();
        System.out.println("노는 시간");
    }
    
    //전략 생성
    private Strategy Eat(final String voice) {
    	return new Strategy() {
    		@Override
        	public void runStrategy() {
        		System.out.println(voice);
        	}
    	};
    }
}

//클라이언트

pacakge 템플릿콜백패턴

public clss Client {
	public static void main(String[] args) {
    	Strategy strategy = null;
        판다 푸바오 = new 판다();
       
        푸바오.runContext("와그작");
        
        푸바오.runContext("딸기는 안먹어");

➕ 스프링 MVC는 Front Controller Pattern(최전선 제어자 패턴)과 MVC(Model-View-Controller)패턴을 활용


김종민, '스프링 입문을 위한 자바 객체 지향의 원리와 이해', 위키북스 참고

profile
DDeo99

0개의 댓글