디자인패턴이란?
객체지향 프로그램 개발시 적용할 수 있는 방법론
필수적으로 지켜야하는 룰이 아니라, 수많은 개발자들의 경험과 아이디어가 모여서 집대성된 가이드라인
왜 필요한가?
프로그램을 개발할 때 동작은 되는데 이것이 정답인지 고민한 적이 없으신가요?
굉장히 많은 로직들이 뭉쳐져 있는 클래스를 유지보수하면서 힘드신 적이 없으셨나요?
신입일 때 java의 io 패키지를 다루면서 코드를 왜이렇게 써야하는지 궁금한 적이 없으셨나요?
디자인패턴은 추상화, 캡슐화, 다형성, 상속을 활용하여 확장이 용이한 프로그램을 설계하도록 도와줍니다.
디자인패턴을 배우고나면 io 패키지에 Decorator패턴이 적용되어 있다는 것을 깨닫게 됩니다.
종류?
- Creational : 상황에 따른 알맞은 객체를 생성하는 방법
- Structural : 객체간의 구조와 관계를 설계하는 방법
- Behavioural : 객체 간의 커뮤니케이션 방법
첫 번째 포스팅은 전략(Strategy) 패턴으로 시작하겠습니다.
1. Definition
Define a family of algorithms, encapsulate each one, and make them interchangeable.
Define an common interface(or abstract class) to all related algorithms.
Strategy lets the algorithms vary independently from clients that use it.
- GoF Design Pattern에서 발췌
- 알고리즘 집합을 정의하고, 각자 캡슐화하여 상호교환하게 작성
- 알고리즘에 대한 공통 인터페이스를 정의하고 자세한 알고리즘은 구체 클래스에서 작성
- Client는 런타임에서 원하는 알고리즘을 선택(Delegation)하여 사용
- SOLID 원칙 중 OCP(Open-Close principal, 확장에는 열려있으나 변경에는 닫혀있어야 한다)를 준수
*) 결국 Client가 필요한 전략(알고리즘)을 자유롭게 채택 및 변경하여 사용하는 디자인패턴
2. Structure
- 전략(Strategy) : 알고리즘 집합의 상위 인터페이스
- 전략 사용자(Context) : 전략을 사용하는 프로그램의 문맥
- 전략 제공자(Client) : 전략 사용자(Context)에서 사용할 전략을 선택
3. Example
- 암호화에 대한 프로그램을 작성한다고 가정
- AES128, SHA256, MD5 등 수많은 암호화 알고리즘이 존재
the worst case)
public class Cipher {
public byte[] encrypt(String type, byte[] key, byte[] iv, byte[] text) throws Exception {
byte[] result = new byte[0];
if ("AES128".equals(type)) {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCSPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),
new IvParameterSpec(iv));
result = cipher.doFinal(text);
} else if ("SHA256".equals(type)) {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(iv);
md.update(text);
result = md.digest();
}
return result;
}
}
- 암호화 로직이 추가될 때마다 encrypt()의 복잡도가 증가(변경에 닫혀있지 않음)
- encrypt()를 수정하지 않고서는 확장할 수 있는 방법이 없음(확장에 열려있지 않음)
better case)
/*
* 암호화(encrypt)의 알고리즘 그룹의 최상위 인터페이스 정의
*/
public interface Encryption {
public byte[] encrypt(byte[] key, byte[] iv, byte[] text) throws Exception;
}
/*
* 상위 인터페이스에 맞추어 구현한 AES128 알고리즘
*/
public class AES128Encryption implements Encryption {
@Override
public byte[] encrypt(byte[] key, byte[] iv, byte[] text) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCSPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),
new IvParameterSpec(iv));
return cipher.doFinal(text);
}
}
/*
* 상위 인터페이스에 맞추어 구현한 SHA256 알고리즘
*/
public class SHA256Encryption implements Encryption {
@Override
public byte[] encrypt(byte[] key, byte[] iv, byte[] text) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(iv);
md.update(text);
return md.digest();
}
}
/*
* 암호화(Encryption)을 상속하지 않고, 구성요소(Composition)으로 포함
* 암호화 인터페이스의 변경사항이 해당 Cipher context에 영향을 주지 않음
* Cipher 입장에서는 어느 암호화 알고리즘을 사용하는지 신경쓰지않고, encrypt()만 호출(delegation)
*/
public class Cipher {
private Encryption encryption;
public void setEncryption(Encryption encryption) {
this.encryption = encryption;
}
public byte[] encrypt(byte[] key, byte[] iv, byte[] text) throws Exception {
return encryption.encrypt(key, iv, text);
}
}
public static void main(String[] args) {
String key = "mwav";
String iv = "net";
String text = "Aitibai Damir";
Cipher cipher = new Cipher();
cipher.setEncryption(new AES128Encryption());
cipher.encrypt(key, iv, text);
cipher.setEncryption(new SHA256Encryption());
cipher.encrypt(key, iv, text);
}
1) 암호화(Encryption) 알고리즘의 상위 인터페이스 정의
2) 해당 인터페이스에 맞추어 각 암호화 알고리즘 로직 구현
3) Context 설정
- Encryption을 구성요소로 사용(Encryption의 변경점이 Context에 영향을 주지 않음)
- Cipher 입장에서는 암호화 알고리즘의 세부 내용을 모르는 상태에서 encrypt()만 호출(Delegation)
4) Client에서 원하는 알고리즘을 선택(new AES128Encryption())하여 Context에 주입
*) Cipher를 추상 클래스로 작성하여, 특정 암호화에 대한 프로필로도 활용할 수 있음
public abstract class Cipher {
private Encryption encryption;
public void setEncryption(Encryption encryption) {
this.encryption = encryption;
}
public byte[] encrypt(byte[] key, byte[] iv, byte[] text) throws Exception {
return encryption.encrypt(key, iv, text);
}
}
/**
* 해당 context는 생성시 자동으로 AES128 알고리즘을 사용
* setEncryption()를 호출하여 알고리즘 변경도 가능
*/
public class AES128Cipher extends Cipher {
public AES128Cipher() {
setEncryption(new AES128Encryption());
}
}
- 새로운 암호화 알고리즘을 추가한다면?
- 만약 복호화(decrypt)를 지원한다면?
- sns oauth2 login(naver, kakao, google) 등에 활용한다면?
4. Pros vs Cons
*) Pros
- Strategy 객체와 Context 객체의 느슨한 결합(Strategy 객체를 작성할 때, 알고리즘 자체에만 집중할 수 있음)
- 새로운 Strategy를 추가하기 쉬우며, 이 때 기존 클래스(Context, Client)는 영향을 받지 않음
*) Cons
- Strategy 객체에 전달된 인자의 일부가 구현체 내용에 따라서는 의미없거나, 사용되지 않을 수 있음
'Design Pattern' 카테고리의 다른 글
Adapter(How slf4j works?) (0) | 2022.05.07 |
---|---|
Decorator (0) | 2022.03.15 |
Observer (0) | 2022.03.02 |