๐ Chain Of Responsibility ํจํด - ์๋ฒฝ ๋ง์คํฐํ๊ธฐ
Chain Of Responsibility Pattern
์ฑ ์ ์ฐ์ ํจํด(Chain Of Responsibility Pattern, COR)์ ํด๋ผ์ด์ดํธ์ ์์ฒญ์ ๋ํ ์ธ์ธํ ์ฒ๋ฆฌ๋ฅผ ํ๋์ ๊ฐ์ฒด๊ฐ ๋ชฝ๋ ํ๋ ๊ฒ์ด ์๋, ์ฌ๋ฌ๊ฐ์ ์ฒ๋ฆฌ ๊ฐ์ฒด๋ค๋ก ๋๋๊ณ , ์ด๋ค์ ์ฌ์ฌ(chain) ์ฒ๋ผ ์ฐ๊ฒฐํด ์งํฉ ์์์ ์ฐ์์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ํ๋ ํจํด์ด๋ค.
์ด๋ฌํ ์ฒ๋ฆฌ ๊ฐ์ฒด๋ค์ ํธ๋ค๋ฌ(handler)๋ผ๊ณ ๋ถ๋ฅด๋๋ฐ, ์์ฒญ์ ๋ฐ์ผ๋ฉด ๊ฐ ํธ๋ค๋ฌ๋ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋์ง, ์์ผ๋ฉด ์ฒด์ธ์ ๋ค์ ํธ๋ค๋ฌ๋ก ์ฒ๋ฆฌ์ ๋ํ ์ฑ ์์ ์ ๊ฐํ๋ค. ํ๋ง๋๋ก ์ฑ ์ ์ฐ์๋ผ๋ ๋ง์ ์์ฒญ์ ๋ํ ์ฑ ์์ ๋ค๋ฅธ ๊ฐ์ฒด์ ๋ ๋๊ธด๋ค๋ ์๋ฆฌ์ด๋ค. ๋ ๋๊ธด๋ค๊ณ ํ๋๊น ๋ถ์ ์ ์ธ ์๋ฏธ๋ก ๋ค๋ฆด์๋ ์๊ฒ ์ง๋ง, ์ด๋ฌํ ์ฒด์ธ ๊ตฌ์ฑ์ ํ๋์ ๊ฐ์ฒด์ ์ฒ๋ฆฌ์ ๋ํ ์ฑ ์์ ์์ฒญ์ ๋ณด๋ด๋ ์ชฝ(sender)๊ณผ ์์ฒญ์ ์ฒ๋ฆฌํ๋(receiver) ์ชฝ์ ๋ถ๋ฆฌํ์ฌ ๊ฐ ๊ฐ์ฒด๋ฅผ ๋ถํ์ผ๋ก ๋ ๋ฆฝ์ํค๊ณ ๊ฒฐํฉ๋๋ฅผ ๋์จํ๊ฒ ๋ง๋ค๋ฉฐ, ์ํฉ์ ๋ฐ๋ผ์ ์์ฒญ์ ์ฒ๋ฆฌํ ๊ฐ์ฒด๊ฐ ๋ณํ๋ ํ๋ก๊ทธ๋จ์๋ ์ ์ฐํ๊ฒ ๋์ํ ์ ์๋ค๋ ์ฅ์ ์ ๊ฐ์ง๊ณ ์๋ค. ํนํ๋ ์ค์ฒฉ if-else๋ฌธ๋ค์ ์ต์ ํํ๋๋ฐ ์์ด ์ค๋ฌด์์๋ ๋ง์ด ์ ์ฉ๋๋ ํจํด์ค ํ๋์ด๊ธฐ๋ ํ๋ค.
์ข ๋ ์ ๊ทผํ๊ธฐ ์ฝ๊ฒ ์ค์ํ๋ก ๋น์ ๋ฅผ ๋ ๋ค๋ฉด, ์๋ฅผ๋ค์ด ์๋น์๊ฐ ๋ฌธ์๋ฅผ ์ํด ๊ณ ๊ฐ์ผํฐ์ ์ ํ๋ฅผ ๊ฑธ์๋ค๊ณ ํด๋ณด์. ๊ทธ๋ฌ๋ฉด ์๋์ ๊ฐ์ ์์๋ก ์์ฒญ์ ์ฒ๋ฆฌํด๋ณธ ๊ฒฝํ์ ํ ๋ฒ ์ฉ์ ๊ฒช์ด ๋ดค์ ๊ฒ์ด๋ค.
- ์ฒซ ๋ฒ์งธ๋ก, ์๋ ์๋ต๊ธฐ ์์ฑ ๋ก๋ด์ด ์๋ตํ๊ฒ ๋๋ค. ๊ทธ๋ฐ๋ฐ ๋ง์ผ ์์ฑ ๋ก๋ด์ด ์ ์ํ๋ ํด๊ฒฐ์ฑ ์ ๋ํด์ 1๋ถํฐ 4๊น์ง ์ ํ์ฌํญ์ค ํด๋นํ๋ ๋ฒํธ๊ฐ ์๋ค๋ฉด ์๋ง ์ฌ๋ฌ๋ถ๋ ๊ทธ๋ ๊ณ ๋๋ค์์ ์ฌ๋๋ค์ด ๊ธฐํ ๋ฒํผ์ ๋๋ฌ ์๋ด์ ์ฐ๊ฒฐ์ ๋๋ฅผ ๊ฒ์ด๋ค.
- ๋ ๋ฒ์งธ๋ก, ์๋ด์ฌ๊ฐ ์ ํ๋ฅผ ๋ฐ์์ง๋ง ๊ธธ๊ฒ ํตํํ ๊ฒฐ๊ณผ ์ ๋๋ก๋ ๊ธฐ์ ์ ์ธ ๋์์ ๋ฐ์ง ๋ชปํ๋ค๊ณ ํ๋ค. ๊ทธ๋ฌ๋ฉด ์๋ด์์ด ์ง์ ์์ง๋์ด์๊ฒ ์ ํ๋ฅผ ์ฐ๊ฒฐํด์ฃผ๊ฒ ๋๋ค.
- ์ธ ๋ฒ์งธ๋ก, ์์ง๋์ด์ ํตํํ๊ณ ์ ํฉํ ์๋ฃจ์ ์ ์ ์ํด์ค์ผ๋ก์จ ๋ฌธ์ ๊ฐ ํด๊ฒฐ๋์ด ํตํ๋ฅผ ์ข ๋ฃํ๋ค.
๋ง์น ์๋น์์ ์์ฒญ์ ๋ํด ๊ฐ ์ฒ๋ฆฌ ์๋น์ค๋ค์ด ๋ฌด์์ธ๊ฐ ์ฒด์ธ์ฒ๋ผ ์ฐ๊ฒฐ๋๋ ํ๋ฆ์ด ๋๋ ๊ฒ ์ฒ๋ผ ๋ณด์ผ ๊ฒ์ด๋ค.
์ฌ๊ธฐ์ ํธ๋ค๋ฌ(Handler)๋ ์๋ ์๋ต๊ธฐ ๋ก๋ด, ์๋ด์ฌ, ์์ง๋์ด๊ฐ ๋๋ค. ์ฆ, ๊ฐ ํธ๋ค๋ฌ์์ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋์ง์ ์ฌ๋ถ๋ฅผ ๋ฐ์ง๊ณ ๋ถ๊ฐ๋ฅํ๋ฉด ๋ค์ ํธ๋ค๋ฌ๋ก ๋ ๋๊ธฐ๋ ๊ณผ์ ์ ์งํํ ๊ฒ์ ์ฝ๋๋ก ๊ตฌํํ ๊ฒ์ด ์ฑ ์ ์ฐ์ ํจํด์ธ ๊ฒ์ด๋ค.
์ฑ ์ ์ฐ์ ํจํด ๊ตฌ์กฐ
- Handler : ์์ฒญ์ ์์ ํ๊ณ ์ฒ๋ฆฌ ๊ฐ์ฒด๋ค์ ์งํฉ์ ์ ์ํ๋ ์ธํฐํ์ด์ค
- ConcreteHandler : ์์ฒญ์ ์ฒ๋ฆฌํ๋ ์ค์ ์ฒ๋ฆฌ ๊ฐ์ฒด
- ํธ๋ค๋ฌ์ ๋ํ ํ๋๋ฅผ ๋ด๋ถ์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ ๋ฉ์๋๋ฅผ ํตํด ๋ค์ ํธ๋ค๋ฌ๋ฅผ ์ฒด์ธ์ํค๊ณ ๋ค์ ๋ฐ๋ผ๋ณธ๋ค.
- ์์ ์ด ์ฒ๋ฆฌํ ์ ์๋ ์๊ตฌ๊ฐ ๋์ค๋ฉด ๋ฐ๋ผ๋ณด๊ณ ์๋ ๋ค์ ์ฒด์ธ์ ํธ๋ค๋ฌ์๊ฒ ์์ฒญ์ ๋ ๋๊ธด๋ค.
- ConcreteHandler1 - ConcreteHandler2 - ConcreteHandler3 - ... ์ด๋ฐ์์ผ๋ก ์ฒด์ธ ํ์์ด ๊ตฌ์ฑ๋๊ฒ ๋๋ค.
- Client : ์์ฒญ์ Handler ์ ๋ฌํ๋ค
์ฌ๊ธฐ์ ํธ๋ค๋ฌ๋ผ๋ฆฌ ์ฒด์ด๋ ๋๋ ๊ตฌ์กฐ๋ ์ด๋ค ํํ์ด๋ ์๊ด์ด ์๋ค. ๋ฆฌ์คํธํ ์ผ์๋ ์๊ณ ์ ํ ์ผ ์๋ ์๊ณ ํธ๋ฆฌ ํํ์ผ ์๋ ์๋ค.
์ฑ ์ ์ฐ์ ํจํด ํน์ง
ํจํด ์ฌ์ฉ ์๊ธฐ
- ํน์ ์์ฒญ์ 2๊ฐ ์ด์์ ์ฌ๋ฌ ๊ฐ์ฒด์์ ํ๋ณํ๊ณ ์ฒ๋ฆฌํด์ผ ํ ๋
- ํน์ ์์๋ก ์ฌ๋ฌ ํธ๋ค๋ฌ๋ฅผ ์คํํด์ผ ํ๋ ๊ฒฝ์ฐ
- ํ๋ก๊ทธ๋จ์ด ๋ค์ํ ๋ฐฉ์๊ณผ ์ข ๋ฅ์ ์์ฒญ์ ์ฒ๋ฆฌํ ๊ฒ์ผ๋ก ์์๋์ง๋ง ์ ํํ ์์ฒญ ์ ํ๊ณผ ์์๋ฅผ ๋ฏธ๋ฆฌ ์ ์ ์๋ ๊ฒฝ์ฐ
- ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์๋ ๊ฐ์ฒด ์งํฉ์ด ๋์ ์ผ๋ก ์ ์๋์ด์ผ ํ ๋ (์ฒด์ธ ์ฐ๊ฒฐ์ ๋ฐํ์์์ ๋์ ์ผ๋ก ์ค์ )
ํจํด ์ฅ์
- ํด๋ผ์ด์ธํธ๋ ์ฒ๋ฆฌ ๊ฐ์ฒด์ ์ฒด์ธ ์งํฉ ๋ด๋ถ์ ๊ตฌ์กฐ๋ฅผ ์ ํ์๊ฐ ์๋ค.
- ๊ฐ๊ฐ์ ์ฒด์ธ์ ์์ ์ด ํด์ผํ๋ ์ผ๋ง ํ๊ธฐ ๋๋ฌธ์ ์๋ก์ด ์์ฒญ์ ๋ํ ์ฒ๋ฆฌ๊ฐ์ฒด ์์ฑ์ด ํธ๋ฆฌํด์ง๋ค.
- ํด๋ผ์ด์ธํธ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ํธ๋ค๋ฌ๋ฅผ ์ฒด์ธ์ ๋์ ์ผ๋ก ์ถ๊ฐํ๊ฑฐ๋ ์ฒ๋ฆฌ ์์๋ฅผ ๋ณ๊ฒฝํ๊ฑฐ๋ ์ญ์ ํ ์ ์์ด ์ ์ฐํด์ง๋ค
- ์์ฒญ์ ํธ์ถ์(invoker)์ ์์ ์(receiver) ๋ถ๋ฆฌ์ํฌ ์ ์๋ค.
- ์์ฒญ์ ํ๋ ์ชฝ๊ณผ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ์ชฝ์ ๋์ปคํ๋ง ์์ผ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๋ค
- ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด ๋ฐ๋๋๋ผ๋ ํธ์ถ์ ์ฝ๋๋ ๋ณ๊ฒฝ๋์ง ์๋๋ค.
ํจํด ๋จ์
- ์คํ ์์ ์ฝ๋์ ํ๋ฆ์ด ๋ง์์ ธ์ ๊ณผ์ ์ ์ดํด๋ณด๊ฑฐ๋ ๋๋ฒ๊น ๋ฐ ํ ์คํธ๊ฐ ์ฝ์ง ์๋ค.
- ์ถฉ๋ถํ ๋๋ฒ๊น ์ ๊ฑฐ์น์ง ์์์ ๊ฒฝ์ฐ ์งํฉ ๋ด๋ถ์์ ๋ฌดํ ์ฌ์ดํด์ด ๋ฐ์ํ ์ ์๋ค.
- ์์ฒญ์ด ๋ฐ๋์ ์ํ๋๋ค๋ ๋ณด์ฅ์ด ์๋ค. (์ฒด์ธ ๋๊น์ง ๊ฐ๋๋ฐ๋ ์ฒ๋ฆฌ๋์ง ์์ ์ ์๋ค)
- ์ฑ ์ ์ฐ์๋ก ์ธํ ์ฒ๋ฆฌ ์ง์ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค. ๋ค๋ง ์ด๋ ํธ๋ ์ด๋ ์คํ๋ก์ ์์ฒญ๊ณผ ์ฒ๋ฆฌ์ ๋ํ ๊ด๊ณ๊ฐ ๊ณ ์ ์ ์ด๊ณ ์๋๊ฐ ์ค์ํ๋ฉด ์ฑ ์ ์ฐ์ ํจํด ์ฌ์ฉ์ ์ ์ํ์ฌ์ผ ํ๋ค.
์์ ๋ฅผ ํตํด ์์๋ณด๋ Chain Of Responsibility ํจํด
์ฑ ์์ ๋ถ๋ฆฌํ์ฌ ์ฐ๊ฒฐ์ง๊ธฐ
์ฌ์ฉ์๋ก๋ถํฐ url ๋ฌธ์์ด์ ์ ๋ ฅ๋ฐ์ผ๋ฉด, ๊ฐ url์ ํ๋กํ ์ฝ, ๋๋ฉ์ธ, ํฌํธ๋ฅผ ํ์ฑํด์ ์ ๋ณด๋ฅผ ์ถ๋ ฅํด์ฃผ๋ ํ๋ก๊ทธ๋จ์ ๋ง๋ ๋ค๊ณ ํด๋ณด์.
ํด๋ฆฐํ์ง ์์ ๋ฌธ์ ์ ์ฝ๋ โ
์ผ๋ฐ์ ์ผ๋ก๋ ์๋์ ๊ฐ์ด ์ฒ๋ฆฌ๋ฌธ์ ํ๋์ ๋ฉ์๋๋ก ํต์ง๋ก ๊ตฌ์ฑํ ๊ฒ์ด๋ค.
class UrlParser {
public static void run(String url) {
// protocol ํ์ฑ
int index = url.indexOf("://");
if (index != -1) {
System.out.println("PROTOCOL : " + url.substring(0, index));
} else {
System.out.println("NO PROTOCOL");
}
// domain ํ์ฑ
int startIndex = url.indexOf("://");
int lastIndex = url.lastIndexOf(":");
System.out.print("DOMAIN : ");
if (startIndex == -1) {
if (lastIndex == -1) {
System.out.println(url);
} else {
System.out.println(url.substring(0, lastIndex));
}
} else if (startIndex != lastIndex) {
System.out.println(url.substring(startIndex + 3, lastIndex));
} else {
System.out.println(url.substring(startIndex + 3));
}
// port ํ์ฑ
int index2 = url.lastIndexOf(":");
if (index2 != -1) {
String strPort = url.substring(index2 + 1);
try {
int port = Integer.parseInt((strPort));
System.out.println("PORT : " + port);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
}
}
class Client {
public static void main(String[] args) {
String url1 = "http://www.youtube.com:80";
System.out.println("INPUT: " + url1);
UrlParser.run(url1);
String url2 = "https://www.inpa.tistory.com:443";
System.out.println("INPUT: " + url2);
UrlParser.run(url2);
String url3 = "http://localhost:8080";
System.out.println("INPUT: " + url3);
UrlParser.run(url3);
}
}
๋์์๋ ๋ฌธ์ ๋ ์์ง๋ง, ์ด๋ฐ์์ผ๋ก ๊ตฌ์ฑํ ๊ฒฝ์ฐ ๋ง์ผ path๋ queryString๋ ๊ตฌํ๋ ์๋ก์ด ์ฒ๋ฆฌ ๋ก์ง์ด ์ถ๊ฐ๋ ๊ฒฝ์ฐ ๋ฉ์๋ ๋ก์ง์ ํต์ง๋ก ์์ ํด์ผํ ๊ฒ์ด๊ณ ์ด์ ์ ๋ก์ง๊ณผ ๊ฒน์น๋๊ฒ ์๋์ง ๋ณต๊ธฐํด์ผ ํ๋ค. ๋น์ฅ ์ฝ๋๋ง ๋ด๋ ํ๋กํ ์ฝ, ๋๋ฉ์ธ, ํฌํธ๋ฅผ ๊ตฌํ๋๋ฐ ์์ด indexOf() ๋ฉ์๋๋ฅผ ์์ฒด์ ์ผ๋ก ์ฌ์ฉํ๊ณ ์์ด ๊ฒน์น๋ ๋ถ๋ถ์ด ์๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
๋ํ ์ฌ์ฉ์๊ฐ ํฌํธ ์ ๋ณด๋ ์ํ์ง ์์ ๊ฒฝ์ฐ ์ฝ๋๋ฅผ ์ง์ธ์๋ ์์ผ๋ ํฌํธ ํ์ฑ ๋ถ๋ถ๋ง ๋บ ๋น์ทํ ์ฝ๋๋ก ์ด๋ฃจ์ด์ง ๋ฉ์๋๋ฅผ ๋ฐ๋ก ์์ฑํด์ผ ํ๋ค. ์ด๋ ์๊ตฌ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ์ค์ ์ง๊ถ์ ์ผ๋ก ๋ชจ๋ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ ํ์์ด๋ค.
์ฑ ์ ์ฐ์ ํจํด์ ์ ์ฉํ ์ฝ๋ โ๏ธ
๊ทธ๋ ๋ค๋ฉด ํ๋กํ ์ฝ๋ง์ ํ์ฑํ๋ ์ฑ ์๊ณผ ๋๋ฉ์ธ๋ง์ ํ์ฑํ๋ ์ฑ ์ ์ด๋ ๊ฒ ์ฒ๋ฆฌ ์ฑ ์์ ๊ฐ ๊ฐ์ฒด๋ค๋ก ๋ถ๋ฆฌํ๊ณ ์ด๋ค ๋ผ๋ฆฌ ์ฒด์ธ์ผ๋ก ์ฐ๊ฒฐํจ์ผ๋ก์จ ์ข๋ ์ ์ฐํ๊ฒ ํ๋ก๊ทธ๋จ ๋ก์ง์ ๊ตฌ์ฑํ ์ ์๊ฒ ๋๋ค. ์ด๋ฅผ ๊ทธ๋ฆผ์ผ๋ก ํํํ์๋ฉด ์๋์ ๊ฐ๋ค.
ํธ๋ค๋ฌ๋ ๋ณธ์ธ์ ์ญํ ๋ง์ ์ํํ๊ณ ์ถ๊ฐ ์ฒ๋ฆฌ ๋ก์ง์ด ํ์ํ๋ค๋ฉด ์ ์ฐํ๊ฒ ์ฒด์ธ์ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ฉฐ ์ฒด์ธ์ ๊ตฌ์กฐ์ ์ผ๋ก ๋ค์ํ๊ฒ ๊ตฌ์ฑํ ์ ์๊ฒ ๋๋ค.
์ฑ ์ ์ฐ์๋ฅผ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ ๊ทธ๋ฆฌ ์ด๋ ต์ง ์๋ค. ๊ธฐ์กด์ ์ฝ๋์์ ๊ฐ ๋ด๋นํ๋ ๋ถ๋ถ์ ๋ฐ๋ก ํธ๋ค๋ฌ(Handler) ๊ฐ์ฒด๋ก ๋ด๋นผ์ฃผ๋ฉด ๋๋ค.
์ ๋ต ํจํด์ด ์ ๋ต ์๊ณ ๋ฆฌ์ฆ ์ฝ๋๋ฅผ ๊ฐ์ฒดํ ํ ๊ฒ์ด๊ณ , ์ํ ํจํด์ ๊ฐ์ฒด์ ์ํ๋ฅผ, ๋ช ๋ น ํจํด์ด ์ปค๋งจ๋๋ฅผ ๊ฐ์ฒดํ ํ ๊ฒ๊ณผ ๊ฐ์ด, ์ฑ ์ ์ฐ์ ํจํด์ ์กฐ๊ฑด๋ฌธ์ ์์ฒญ ์ฒ๋ฆฌ ๋ก์ง ์์ฒด๋ฅผ ๊ฐ์ฒดํ ํ ๊ฒ์ผ๋ก ๋ณด๋ฉด ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์ผ๋ฐ์ ์ธ ์กฐ๊ฑด ๋ถ๊ธฐ๋ฌธ ๊ฐ์ ๊ฒฝ์ฐ ๋ค์ค์ผ๋ก ๊ตฌ์ฑ๋ ์ ์์ผ๋ ์ด๋ฅผ ์ฒด์ธ์ผ๋ก ๊ฐ์ฒด๋ผ๋ฆฌ ์ฐ๊ฒฐํจ์ผ๋ก์จ, ๊ฐ if๋ฌธ ๋ก์ง์ ํด๋์ค๋ก ํํํ์๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
// ๊ตฌ์ฒด์ ์ธ ํธ๋ค๋ฌ๋ฅผ ๋ฌถ๋ ์ธํฐํ์ด์ค (์ถ์ ํด๋์ค)
abstract class Handler {
// ๋ค์ ์ฒด์ธ์ผ๋ก ์ฐ๊ฒฐ๋ ํธ๋ค๋ฌ
protected Handler nextHandler = null;
// ์์ฑ์๋ฅผ ํตํด ์ฐ๊ฒฐ์ํฌ ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋ก
public Handler setNext(Handler handler) {
this.nextHandler = handler;
return handler; // ๋ฉ์๋ ์ฒด์ด๋ ๊ตฌ์ฑ์ ์ํด ์ธ์๋ฅผ ๊ทธ๋๋ก ๋ฐํํจ
}
// ์์ ํธ๋ค๋ฌ์์ ๊ตฌ์ฒดํ ํ๋ ์ถ์ ๋ฉ์๋
protected abstract void process(String url);
// ํธ๋ค๋ฌ๊ฐ ์์ฒญ์ ๋ํด ์ฒ๋ฆฌํ๋ ๋ฉ์๋
public void run(String url) {
process(url);
// ๋ง์ผ ํธ๋ค๋ฌ๊ฐ ์ฐ๊ฒฐ๋๊ฒ ์๋ค๋ฉด ๋ค์ ํธ๋ค๋ฌ๋ก ์ฑ
์์ ๋ ๋๊ธด๋ค
if (nextHandler != null)
nextHandler.run(url);
}
}
class ProtocolHandler extends Handler {
@Override
protected void process(String url) {
int index = url.indexOf("://");
if (index != -1) {
System.out.println("PROTOCOL : " + url.substring(0, index));
} else {
System.out.println("NO PROTOCOL");
}
}
}
class DomianHandler extends Handler {
@Override
protected void process(String url) {
int startIndex = url.indexOf("://");
int lastIndex = url.lastIndexOf(":");
System.out.print("DOMAIN : ");
if (startIndex == -1) {
if (lastIndex == -1) {
System.out.println(url);
} else {
System.out.println(url.substring(0, lastIndex));
}
} else if (startIndex != lastIndex) {
System.out.println(url.substring(startIndex + 3, lastIndex));
} else {
System.out.println(url.substring(startIndex + 3));
}
}
}
class PortHandler extends Handler {
@Override
protected void process(String url) {
int index = url.lastIndexOf(":");
if (index != -1) {
String strPort = url.substring(index + 1);
try {
int port = Integer.parseInt((strPort));
System.out.println("PORT : " + port);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
}
}
class Client {
public static void main(String[] args) {
// 1. ํธ๋ค๋ฌ ์์ฑ
Handler handler1 = new ProtocolHandler();
Handler handler2 = new DomianHandler();
Handler handler3 = new PortHandler();
// 2. ํธ๋ค๋ฌ ์ฐ๊ฒฐ ์ค์ (handler1 → handler2 → handler3)
handler1.setNext(handler2).setNext(handler3);
// 3. ์์ฒญ์ ๋ํ ์ฒ๋ฆฌ ์ฐ์ ์คํ
String url1 = "http://www.youtube.com:80";
System.out.println("INPUT: " + url1);
handler1.run(url1);
System.out.println();
String url2 = "https://www.inpa.tistory.com:443";
System.out.println("INPUT: " + url2);
handler1.run(url2);
System.out.println();
String url3 = "http://localhost:8080";
System.out.println("INPUT: " + url3);
handler1.run(url3);
}
}
์ค์ฒฉ๋ if-else ๋ฌธ ์ฌ๊ตฌ์ฑํ๊ธฐ
์ฒซ๋ฒ์งธ ํจํด ์์ ๋ ์ค์ ์ง๊ถํํ ๋ก์ง์ ํธ๋ค๋ฌ ๊ฐ์ฒด๋ก ๋ถ๋ฆฌํ์ฌ ์ฑ ์์ ๋ถ์ฐ์ํค๋ ๋ฐฉ๋ฒ์ ์๊ฐํ๋ค๋ฉด, ๋๋ฒ์งธ ํจํด ์์ ๋ ์ค์ฒฉ๋ if-else ์กฐ๊ฑด๋ฌธ์ ์ฑ ์ ์ฐ์ ํจํด์ผ๋ก ์ ์ฐํ๋ฉฐ ๊ฐ๋ ์ฑ ์ข๊ฒ ๊ตฌ์ฑํ๋ ์ฌ๋ก๋ฅผ ๋ค์ด๋ณผ ๊ฒ์ด๋ค.
ํด๋ฆฐํ์ง ์์ ๋ฌธ์ ์ ์ฝ๋ โ
์๋ฒ๊ฐ ์๊ณ ์ฌ์ฉ์๊ฐ ๋ฏธ๋ค์จ์ด๋ฅผ ํตํด ๋ก๊ทธ์ธ ํ๋ ๊ณผ์ ์ ์๋ฐ์ ํด๋์ค๋ก ๊ฐ๋จํ๊ฒ ํํํด ๋ณด์๋ค. ์ฝ๋๊ฐ ๊ธธ์ด๋ณด์ฌ ์ด๋ ค์๋ณด์ด์ง๋ง ๊ฐ๋จํ๊ฒ ๊ตฌ์ฑํด๋ณธ ์์ ์ด๋ค. ์๋ฒ๋ ๊ฐ์ข ์ฌ์ฉ์ ์ ๋ณด๋ค์ Map ์๋ฃํํ๋ก ์ง๋๊ณ ์๊ณ , ๋ฏธ๋ค์จ์ด๋ ๊ฐ์ข ๋ก๊ทธ์ธ ์ธ์ฆ ๋ก์ง๋ค์ ๊ด๋ฆฌํ๋ฉฐ, ํด๋ผ์ด์ธํธ์์ ๊ฐ์ข ๋ฏธ๋ค์จ์ด์ boolean ๋ฉ์๋๋ฅผ ํตํด ์กฐ๊ฑด ๋ถ๊ธฐ๋ฌธ์ ํตํด ๋ก๊ทธ์ธ ๊ณผ์ ์ ์ฒ๋ฆฌํ๋ค.
class Server {
private Map<String, String> users = new HashMap<>();
// ์๋ฒ์ ์ ์ ๋ฑ๋ก
public void register(String email, String password) {
users.put(email, password);
}
// ์๋ฒ์ ํด๋น ์ด๋ฉ์ผ์ด ๊ฐ์
๋์ด ์๋์ง
public boolean hasEmail(String email) {
return users.containsKey(email);
}
// ์๋ฒ์ ํด๋น ๊ณ์ ์ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ๋์ง
public boolean isValidPassword(String email, String password) {
return users.get(email).equals(password);
}
}
class Middleware {
private int limit = 3;
private int count = 0;
Server server;
public Middleware(Server server) {
this.server = server;
}
// ๋ก๊ทธ์ธ ํ์ ์๋
public boolean limitLoginAttempt() {
if (count > limit) {
System.out.println("๋ก๊ทธ์ธ ์์ฒญ ํ์ ์ ํ !!");
return false;
}
count++;
return true;
}
// ์์ด๋, ํจ์ค์๋ ์ธ์ฆ
public boolean authorize(String email, String password) {
if (!server.hasEmail(email)) {
System.out.println("๊ฐ์
๋ ์ด๋ฉ์ผ ๊ณ์ ์ด ์์ต๋๋ค !");
return false;
}
if (!server.isValidPassword(email, password)) {
System.out.println("ํจ์ค์๋๊ฐ ๋ค๋ฆ
๋๋ค !");
return false;
}
System.out.println("๋ก๊ทธ์ธ ์๋ฃ !!");
return true;
}
// ์ฌ์ฉ์๊ฐ admin ๊ณ์ ์ธ์ง, ํน์ ์ผ๋ฐ user ๊ณ์ ์ธ์ง ๊ฒ์ฆ
public boolean authentication(String email) {
if (email.equals("inpa@tistory.com")) {
System.out.println("Hello, admin!");
return true;
} else {
System.out.println("Hello, user!");
return false;
}
}
// ๋ง์ผ ๊ด๋ฆฌ์๊ฐ ์๋ ์ผ๋ฐ ์ ์ ์ผ ๊ฒฝ์ฐ ์ ์ ์ ์์ฒญ์ ๋ํด์ ๋ชจ๋ ๋ก๊น
public void logging() {
System.out.println("์์ฒญ์ ๋ก๊น
ํฉ๋๋ค.");
}
}
class Client {
public static void main(String[] args) throws IOException {
// 1. ์๋ฒ ์์ฑ ๋ฐ ๋ฑ๋ก
Server server = new Server();
server.register("john@naver.com", "123123");
server.register("kail@google.com", "456456");
server.register("inpa@tistory.com", "789789");
// 2. ์ธ์ฆ ๋ก์ง์ ์ฒ๋ฆฌํ๋ ๋ฏธ๋ค์จ์ด ์์ฑ
Middleware middleware = new Middleware(server);
// 3. ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ก๊ทธ์ธ ์๋
do {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("\nEmail: ");
String email = reader.readLine();
System.out.print("Password: ");
String password = reader.readLine();
// ๋ง์ผ ๋ก๊ทธ์ธ ํ์๊ฐ ๋จ์์์ ๊ฒฝ์ฐ
if (middleware.limitLoginAttempt()) {
// ์ด๋ฉ์ผ ํจ์ค์๋ ์ธ์ฆ
if (middleware.authorize(email, password)) {
// ์ธ์ฆ๋์์ ๊ฒฝ์ฐ ์ผ๋ฐ ์ ์ ๊ณ์ ์ธ์ง ๊ด๋ฆฌ์ ๊ณ์ ์ธ์ง ๊ฒ์ฆ
if (middleware.authentication(email)) {
// ...
} else {
// ๊ด๋ฆฌ์๊ฐ ์๋ ์ผ๋ฐ ์ ์ ๊ณ์ ์ผ ๊ฒฝ์ฐ ๋ก๊น
์ฒ๋ฆฌ
middleware.logging();
}
break;
} else {
continue;
}
} else {
throw new RuntimeException("๋ก๊ทธ์ธ ์๋ ํ์ ์ด๊ณผ๋ก ํ๋ก๊ทธ๋จ์ ์ข
๋ฃํฉ๋๋ค");
}
} while (true);
}
}
๋์์ ๋ฌธ์ ์์ด ๋ณด์ด์ง๋ง ๋ฑ๋ด๋ ํด๋ผ์ด์ธํธ ์ชฝ ์คํ ์ฝ๋๊ฐ ๊ต์ฅํ ๋ณต์กํ๋ค. ์ฌ๋ฌ ์ธ์ฆ ๋ก์ง๋ค์ ํ๋ํ๋ ๋ฐ์ ธ๊ฐ๋ฉฐ ๋ง์ผ '์ฐธ'์ผ ๊ฒฝ์ฐ ๋ค์ ์ธ์ฆ ๋ก์ง์ ๋ฐ์ ธ๊ฐ๋ ์ค์ฒฉ if-else๋ฌธ ํํ๋ก ๋์ด ์๋ค.
์ด๋ฐ ์ฝ๋๋ ์ฝ๋๋ฅผ ๋ณต๊ธฐํ ๋๋ ํ ์คํธํ ๋ ๊ต์ฅํ ๋ํดํ๊ฒ ๋๋ฉฐ ํนํ๋ if๋ฌธ ์ค์ฒฉ์ ๋จ๊ณ๊ฐ ๋์ด๋ ์๋ก ์ค์ฒฉ ๋ธ๋ก์ด ๋ถ๊ธฐ์์ ์์ ์ ์ผ๋ก ์๋ํ๋์ง ํ์ ํ๊ธฐ๋ ์ด๋ ต๋ค. ๊ฑฐ๊ธฐ๋ค ๋ง์ผ ์๋ก์ด ์ธ์ฆ ๋ฉ์๋๊ฐ ์ถ๊ฐ๋๋ค๋ฉด ๋ถ๊ธฐ๋ฌธ ์ ์ฒด๋ฅผ ๋ฏ์ด ๊ณ ์ณ์ผ ๋ ์ ์๋ค.
์ฑ ์ ์ฐ์ ํจํด์ ์ ์ฉํ ์ฝ๋ โ๏ธ
๋ฌผ๋ก ๊ฐ ์ธ์ฆ ๋ก์ง๋ค์ด ๋ณ๊ฐ์ ๋ฉ์๋๋ก ๋ถ๋ฆฌ๋์ด ์์ง๋ง, ๊ฒฐ๊ตญ ์คํ๋ถ์์ ๋ณต์กํ if-else๋ก ๊ตฌ์ฑ๋๋๊ฑด ๋ง์ฐฌ๊ฐ์ง์ด๊ณ ํ๋์ ๋ฏธ๋ค์จ์ด ํด๋์ค์์ ์ค์ ์ง๊ถ ์ ์ธ ์ฑ ์์ ์ง๋๊ณ ์๊ธฐ ๋๋ฌธ์, ์ฑ ์ ์ฐ์ ํจํด์ผ๋ก ์ธ์ฆ ๋ก์ง๋ค์ ๋ณ๊ฐ์ ํธ๋ค๋ฌ ํด๋์ค๋ก ๋ถ๋ฆฌํ๊ณ ์ฒด์ด๋ ํจ์ผ๋ก์จ if-else ํํ๋ฅผ ๋ณด๋ค ์ ์ฐํ๊ฒ ๊ตฌ์ฑํ ์๊ฐ ์๊ฒ ๋๋ค.
์ด๋ฒ ํจํด ์ ์ฉ ์์ ๋ ์ฒซ๋ฒ์งธ ์์ ์๋ ์ฝ๊ฐ ์ฐจ์ด๋ฅผ ๋์๋๋ฐ, ํธ๋ค๋ฌ ๋ฉ์๋๋ฅผ ์ถ์ ๋ฉ์๋๋ก ๊ฐ ๊ฐ์ฒด๋ณ๋ก ๊ตฌ์ฒดํ ํ๋ ๊ฒ์ด ์๋, ๋ถ๋ชจ์ check() ๋ฉ์๋๋ฅผ ์์ ํธ๋ค๋ฌ์์ ์ค๋ฒ๋ผ์ด๋(Override)ํ๊ณ , ๋ง์ผ ์ฒด์ธ ํธ์ถ์ด ํ์ํ ๊ฒฝ์ฐ super ํค์๋๋ฅผ ํตํด ๋ถ๋ชจ ๋ฉ์๋๋ฅผ ํธ์ถํจ์ผ๋ก์จ ๋ค์ ํธ๋ค๋ฌ๋ฅผ ํธ์ถํ๋, ์๋ฐ ํด๋์ค์ ์์ ํน์ง์ ์ด์ฉํ์๋ค๋ผ๊ณ ๋ณผ ์ ์๋ค.
๋ํ ์์ฒญ์ ๋ํ ๋ก์ง ๋ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ชจ๋ ํธ๋ค๋ฌ ํด๋์ค์๊ฒ ์์ ํ์ผ๋, ๋ณต์กํ if-else ๋ถ๊ธฐ์ ๋ฐ๋ฅธ ๋ก์ง์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ํธ๋ค๋ฌ์์ flag ๋ผ๋ ์ ์๋ฅผ ๋ฐํํ๋๋ก ํ์ฌ, ์คํ๋ถ์์ ์ ์๊ฐ์ ๋ฐ๋ผ ๋ฃจํ๋ฌธ ๋์์ ์ ์ดํ๋ ์์ผ๋ก ๊ตฌ์ฑํ์๋ค.
// Handler ์ญํ
abstract class Middleware {
// ๋ค์ ์ฒด์ธ์ผ๋ก ์ฐ๊ฒฐ๋ ํธ๋ค๋ฌ
protected Middleware nextMiddleware = null;
// setter ๋ฉ์๋๋ฅผ ํตํด ์ฐ๊ฒฐ์ํฌ ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋ก
public Middleware setNext(Middleware middleware) {
this.nextMiddleware = middleware;
return middleware; // ๋ฉ์๋ ์ฒด์ด๋ ๊ตฌ์ฑ์ ์ํด ์ธ์๋ฅผ ๊ทธ๋๋ก ๋ฐํํจ
}
// ํธ๋ค๋ฌ๊ฐ ์์ฒญ์ ๋ํด ์ฒ๋ฆฌํ๋ ๋ฉ์๋
public short check(String email, String password) {
/*
flag๋ ํด๋ผ์ด์ธํธ ์คํ๋ถ์์ while๋ฌธ์ ๋น ์ ธ๋๊ฐ๊ธฐ ์ํ ์กฐ๊ฑด๊ฐ์ผ๋ก ์ฌ์ฉ๋๋ค
-2 : Exception ์ผ์ผํด
-1 : break ๋์ (๋ฃจํ๋ฌธ ์ข
๋ฃ)
0 : continue ๋์ (์ฒ์๋ถํฐ ์ฝ์ ์
๋ ฅ ๋ฐ๊ธฐ)
1 : ๊ทธ๋๋ก ์ฒ๋ฆฌ๋ฅผ ์งํํจ
*/
short flag = 1;
// ๋ง์ผ ํธ๋ค๋ฌ๊ฐ ์ฐ๊ฒฐ๋๊ฒ ์๋ค๋ฉด ๋ค์ ํธ๋ค๋ฌ๋ก ์ฑ
์์ ๋ ๋๊ธด๋ค
if (nextMiddleware != null) {
flag = nextMiddleware.check(email, password);
}
return flag;
}
}
// ConcreteHandler ์ญํ
class LimitLoginAttemptMiddleware extends Middleware {
private int limit = 3; // ๋ก๊ทธ์ธ ์๋ ์ต๋ ํ์ 3ํ
private int count = 0;
@Override
public short check(String email, String password) {
short flag = 1;
if (count > limit) {
System.out.println("๋ก๊ทธ์ธ ์์ฒญ ํ์ ์ ํ !!");
flag = -2; // Exception ํ๋๊ทธ
} else {
flag = super.check(email, password);
}
count++;
return flag;
}
}
class AuthorizeMiddleware extends Middleware {
private Server server;
public AuthorizeMiddleware(Server server) {
this.server = server;
}
@Override
public short check(String email, String password) {
short flag = 1;
if (!server.hasEmail(email)) {
System.out.println("This email is not registered!");
flag = 0; // continue ํ๋๊ทธ
} else if (!server.isValidPassword(email, password)) {
System.out.println("Wrong password!");
flag = 0; // continue ํ๋๊ทธ
} else {
flag = super.check(email, password);
}
return flag;
}
}
class AuthenticationMiddleware extends Middleware {
@Override
public short check(String email, String password) {
short flag = 1;
if (email.equals("inpa@tistory.com")) {
System.out.println("Hello, admin!");
flag = -1; // break ํ๋๊ทธ
} else {
System.out.println("Hello, user!");
flag = super.check(email, password);
}
return flag;
}
}
class LoggingMiddleware extends Middleware {
@Override
public short check(String email, String password) {
System.out.println("์์ฒญ์ ๋ก๊น
ํฉ๋๋ค.");
return -1; // break ํ๋๊ทธ
}
}
class Client {
public static void main(String[] args) throws IOException {
// 1. ์๋ฒ ์์ฑ ๋ฐ ๋ฑ๋ก
Server server = new Server();
server.register("john@naver.com", "123123");
server.register("kail@google.com", "456456");
server.register("inpa@tistory.com", "789789");
// 2. ์ธ์ฆ ๋ก์ง์ ์ฒ๋ฆฌํ๋ ํธ๋ค๋ฌ ์์ฑ
LimitLoginAttemptMiddleware middleware1 = new LimitLoginAttemptMiddleware();
AuthorizeMiddleware middleware2 = new AuthorizeMiddleware(server);
AuthenticationMiddleware middleware3 = new AuthenticationMiddleware();
LoggingMiddleware middleware4 = new LoggingMiddleware();
// 3. ํธ๋ค๋ฌ ์ฒด์ธ
middleware1
.setNext(middleware2)
.setNext(middleware3)
.setNext(middleware4);
// 4. ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ก๊ทธ์ธ ์๋
do {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("\nEmail: ");
String email = reader.readLine();
System.out.print("Password: ");
String password = reader.readLine();
// ํธ๋ค๋ฌ๋ถํฐ ์ ์ flag๋ฅผ ๋ฐ์, ์ ์๊ฐ์ ๋ฐ๋ผ ๋ฃจํ๋ฌธ ๋ค์ ๋์์ ์ฒ๋ฆฌ
short result = middleware1.check(email, password);
if (result == -2) {
throw new RuntimeException("๋ก๊ทธ์ธ ์๋ ํ์ ์ด๊ณผ๋ก ํ๋ก๊ทธ๋จ์ ์ข
๋ฃํฉ๋๋ค");
} else if (result == -1) {
break;
} else if (result == 0) {
continue;
}
} while (true);
}
}
์คํ๋ ค ๊ธฐ์กด ์ฝ๋๋ณด๋ค ๋์์ธ ํจํด์ ์ ์ฉํ ์ฝ๋๊ฐ ์๋ ๋ง์์ง๊ณ ๋ณต์ก์ฑ์ด ์ฆ๊ฐํ ๊ฒ์ฒ๋ผ ๋๋ ์ ์๊ฒ ์ง๋ง, ์คํ๋ถ๋ฅผ ์ข๋ ํ๋์ ์ ์ ์๊ฒ ์ฌํํ๊ฒ ์ฒ๋ฆฌํ๋ค๋ผ๋ ์ ์์ ์์๋ฅผ ๊ฐ์ง ์ ์๋ค.
์ค๋ฌด์์ ์ฐพ์๋ณด๋ Chain Of Responsibility ํจํด
Java
- java.util.logging.Logger์ log()
- javax.servlet.Filter์ doFilter()
์ฌ์ฉ์๊ฐ ์์ฒญ์ ์๋ธ๋ฆฟ์ ์ ๊ทผํ๊ธฐ ์ ์ ๋ค์ํ ํํฐ๋ฅผ ๊ฑฐ์น๋๋ก ํ ์ ์๋ค. ๋ง์ฝ ์๋์ ๊ฐ์ ์ปจํธ๋กค๋ฌ๊ฐ ์๊ณ , "/hello"๋ก ์์ฒญ์ ๋ฐ์ผ๋ฉด ํํฐ๋ฅผ ํ๋๋กํ๋ค.
@ServletComponentScan
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
@WebFilter(urlPatterns = "/hello") // ํน์ url ์๋ธ๋ฆฟ์ ์ ๊ทผํ ์ ๋ค์ ํํฐ๋ฅผ ์ ์ฉ
public class MyFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("๊ฒ์ ์ฐธ์ฌ");
chain.doFilter(request, response);
System.out.println("๊ฒ์ ๋");
}
}
Spring Framework
์คํ๋ง ํ๋ก์ ํธ์ Spring Securiry ์์กด์ฑ์ ์ถ๊ฐํ๊ณ ์๋์ ๊ฐ์ด Security ํํฐ์ ๋ํ ์ฒด์ธ์ ์ค์ ์ ํ ์ ์๋ค.
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ์ฒด์ธ ํํฐ๋ฅผ ์ค์ ํ๋ ์ฝ๋
@Override
protected void configure(HttpSecurity http) throws Exception {
// ๋ชจ๋ ํ์ด์ง์ ์ ๊ทผ๊ถํ์ ์ธ์ฆ์์ด ํ๊ฐ
http.authorizeRequests().anyRequest().permitAll().and().addFilter(new MyFilter());
}
}
# ์ฐธ๊ณ ์๋ฃ
์ฝ๋ฉ์ผ๋ก ํ์ตํ๋ GoF์ ๋์์ธ ํจํด - ๋ฐฑ๊ธฐ์
https://refactoring.guru/design-patterns/chain-of-responsibility
GIS DEVELOPER ์ ํ๋ธ - Chain of Responsibility