본문 바로가기
캠퍼스/Java

62.JAVA 디자인 패턴

by Sylar 2022. 9. 26.

자바 디자인 패턴  ( 에자일 = 유연성 )

디자인 패턴 ? : 코드를 아름답게 짜는 기술 ( 요구사항에 맞게 코딩할수 있는 능력 필요 )

 - 처음부터 완벽한 코드는 없음.
 - 리팩토링 (계발단계) < 필수
 - 에자일은 개발단계를 말하는게 아님. 프로덕션 단계에서 필요함.
   - > 프로덕션 ? : 서비스단계로 생각하면 됨
   - > 프로덕션 단계에서 새로운 기능을 추가할때, 에자일이 없으면 추가하기 힘듦.

 

총 4개만 적을거.

 

1.싱글톤

package singleton;

public class President {
	private static President instance = new President("홍길동");
	public static President getInstance() {
		return instance;
	}
	private String name;
	private President(String name) {
		this.name = name;
	}
}

private static President instance = new President("홍길동");

 instance에 President를 new 시켜서 static공간에 저장. private로 접근을 막았다.


public static President getInstance() {
 return instance;
}

위에서 설정한 instance에 접근 가능한 getter를 만든것과 비슷하다 생각하면 됨.

 

package singleton;

public class App {
	public static void main(String[] args) {
		President p1 = President.getInstance();
		President p2 = President.getInstance();
		
		System.out.println(p1);
		System.out.println(p2);
	}
}

 

저상태에서 Presidente를 다른이름으로 new 시켜도 같은값이 나온다.

 

new로 새로 띄우려고 해도 private가 걸려있어 불가.

 

그래서 이걸 어디다 써먹냐?

UserService가 있는데 Dao가 UserDao, BoardDao 두개가 있다고 가정하자.

BoardDao와 UserDao는 각각의 CRUD를 들고있다.

UserService에 기능은 회원 목록보기, 회원이 쓴 게시글 전체목록 두가지 기능이 있다고 가정.

이 Dao들을 싱글톤으로 관리하면 각 기능별로 new를 계속 해줄 필요없이, 한번만 new시켜서 땡겨 쓸수 있다.

간단하게 말하면, 회원 목록보기를 하는 사람이 100 명이라 치면

싱글톤이 없다면 Serviece를 100개, UserDao를 100개 이렇게 new를 해서 받아야한다.

( 100명이 들어올 경우, Controller에서 Service 100번 new되고, service에서 UserDao를 100번 new 해야한다는 소리.)

그러면 가비지 컬랙션도 200번 일어남 - > 메모리의 낭비가 심해진다

싱글톤으로 하면 재활용 해서 쓰기 때문에, 메모리에 떠있는 service와 userDao를 쓰기 때문에 메모리의 낭비가 사라진다.

 

2. 전략패턴 ( strategy )

 

 

final로 잡으면 변수명은 대문자로 적음

 

 

make를 호출 하면 NAME이 리턴됨. 즉 커피를 만들게 되면 아메리카노가 나오게 만들었다는 소리.

 

 

아메리카노를 내려줄 커피머신을 만들었다.

 

상속을 하면 사용할순 있겠지만, 다형성에 어긋나기 때문에 안됨

 

생성자를 주입받거나 brew에 new해서 인수로 주입받아도 됨.

 

생성자를 주입받아서 커피머신에서 아메리카노를 나오게 만들었다.

 

 

CoffeeMachine에 brew 메서드는 아메리카노 타입으로 지정했으니 아메리카노 타입을 넣어줘야 함.

 

 

아메리카노 완성.

 

 

카페라떼를 추가해서 만들거다.

 

여기서부터 문제가 생긴다.

 

 

이런식으로 커피머신이라는 본 코드에 오버로딩를 통해서 집어넣을수도 있겠지만, 전략패턴에선 이렇게 하면 안됨.

 

OCP (Open Closed Principle) 원칙 - 뭔가 새로운 기능이 생기면, 본코드를 절때 수정시키지 않는다는 원칙.

 

새로운 파일을 만들어내는것은 허락하나, 본코드를 수정하지 말고 추가해야 한다는 소리.

 

저렇게 만들면 OCP 원칙에 어긋난다.


이걸 지키려면 전략패턴을 지켜야 한다.

즉 새로운 커피종류를 추가하면, CoffeeMachine에 추가하지 말고 기능을 해야한다는 소리.

 

 

Coffee라는 인스턴스를 만들고 내부에 아메리카노와 카페모카가 들고있는 메서드를 추가했다.

 

 

카페모카와 아메리카노에 Coffee 를 implements했다.

 

 

커피머신의 brew 타입도 Coffee를 의존하도록 바꾸었다.

 

정상적으로 출력되는 모습.

 

 

구체적인게 아닌 추상적인것을 의존하면, 하나만 의존하게 만들 수 있다.

이걸 왜 해야하나? 

이런식으로 필요한 클래스를만들어 App에 추가하기만 하면 커피머신을 수정 할 필요 없이 다른걸 추가할 수 있다.


전략패턴을 지키면 두가지 효과가 있다.
1. OCP 지킬수 있음. 
2. DIP를 지킬수 있음.
 - DIP? : 의존관계 역전 원칙. 구체적인게 아닌 추상적인것에 의존해야 한다는 소리.

 

3.어뎁터 패턴

 

 - 노트북을 구매했는데 110v짜리다. 근데 우리나라 표준은 220v다.
  : 기존에 변경할수 없는것 = 노트북이 110v인거, 우리나라 표준 220v인거 두개는 못고침.
  => 220v변환잭으로 노트북을 돌려야함.

즉, 기존의 것들을 그대로 둔채, 변화를 주는것을 의미함.

 

 

 

프로그래밍으로 치면 이런느낌.

기존기능에서 값을 Integer로 바꿔서 DB에 저장해야 하는경우, 기존의 String을 Insert하는 기능을 그대로 두고

사이에 Integer로 변환하는 것만 추가해서 사용한다는 소리.

 

커피머신에 있던걸 그대로 복사해서 쓸꺼.

 

 


아메리카노에 시럽을 추가하려고 하는데 이렇게 적으면 OCP 위반임.

 

그럼 새로운 시럽추가한 아메리카노를 만들면 안되나?

 - >되기는 되나 시럽하나 추가하려고 클래스 하나 더만드는건 비효율적.

 

 

아메리카노 어뎁터를 만들고, 타입을 일치시키기 위해 커피를 임플리먼츠 해야함.

 

 

 

private Americano americano;
	
public AmericanoAdapter(Americano americano) {
	this.americano = americano;
}

AmericanoAdapter가 Americano를 참조하여

@Override
public String make() {
	System.out.println("시럽추가");
	return americano.make();
}

행위를 추가할수 있게 만들었다.

 

아메리카노의 클래스를 활용하여 시럽추가라는 내용을 추가하겠다는 소리

 

 

private Americano americano;
	
public AmericanoAdapter(Americano americano) {
	this.americano = americano;
}

m.brew에 해당하는 부분을 집어넣은거.

 

여기서의 핵심은 아메리카노의 코드를 재활용 하여 다른 기능을 추가해서 넣은것임.

새로운 기능을 추가할때, 어뎁터를 활용해서 추가하면 된다.

 

 

이러한 기능을 하나 만들어볼꺼.

클레임과 사과 이메일은 둘다 행위다. 메서드 두개 만들어야함.

 

 

 

 

이런식으로 acceptClaim이 트리거가 되어, sendEmail 메서드를 실행시켜야함.

 

 


이 기능을 사용하기 위해서는 email이라는 라이브러리가 필요하다.

근데 이 라이브러리가 아직 제작중이라면, 임시로 테스트 해볼수 있다

이걸 가짜어뎁터 (MockAdapter)를 사용하여 테스트 해볼꺼.

 

 

1.Email을 EmailSend라는 인터페이스로 구현해서 만들어야함

 

 

2. EmailAdapter 만들기

 

 

이렇게 return을 넘겨줘서 테스트만 해볼꺼.

 

 

테스트를 해보니 재대로 작동한다.

 

만약 재대로 된 라이브러리가 만들어졌다?

 

 

이런식으로 바꿔주기만 하면 됨.

 

단, 가짜객체와 똑같은 방법으로 만들어져야함. ( Email을 EmailSend라는 인터페이스로 구현해서 만들어야함 )

 

4. 프록시 패턴

 

 


서버가 공격으로부터 안전해짐. ( 아이피 공유할 필요 x )

서버 앞단에 위치하여 전후 제어가 가능함. ( 스프링의 인터셉터와 같음 )
 -> 서버에 접근할때 프록시 서버를 통과해서 요청하고, 서버는 프록시 서버를 통해서 출력을 한다는 소리.

 

프록시 서버는 서버 앞에 만들어서 서버를 방어해주는 역할을 할 수 있다.

 

 

간단하게 서버를 만들었다.

 


생성자 만들고 어뎁터랑 똑같이 만들었다.

 

서버를 implements 해서 request에 오버라이드를 달았다.

 

 

ProxyServer에 적힌대로 출력되는 모습.

 


어뎁터랑 뭐가 다른가?


어뎁터는 커피머신을 보고 데이터를 주고 받지만,

프록시는 서버가 아닌 프록시서버를 보고 데이터를 주고받는다.

 

어뎁터는 새로운걸 하나 만들어서 기존의 기능을 의존하여 새로운 기능을 추가한거.

어뎁터는 새로운걸 만든거고, 프록시는 대리인을 만들어 둔거.

즉, 어뎁터와 프록시 서버는 바라보는 대상이 다르다.

 




'캠퍼스 > Java' 카테고리의 다른 글

61. 스레드  (0) 2022.08.23
60.gson  (0) 2022.08.23
59. 소켓통신  (0) 2022.08.23
58.Try, Catch  (0) 2022.08.23
57.DB와 JAVA 연결하기  (0) 2022.08.19