어댑터는 호환되지 않는 인터페이스를 가진 객체들이 협업할 수 있도록 하는 구조적 디자인 패턴입니다.
어댑터는 호출을 받으면 들어오는 XML 데이터를 JSON 구조로 변환한 후 해당 호출을 래핑된 분석 객체의 적절한 메서드들에 전달합니다.
객체 어댑터
이 구현은 객체 합성 원칙을 사용합니다. 어댑터는 한 객체의 인터페이스를 구현하고 다른 객체는 래핑합니다.
클라이언트는 프로그램의 기존 비즈니스 로직을 포함하는 클래스입니다.
클라이언트 인터페이스는 다른 클래스들이 클라이언트 코드와 공동 작업할 수 있도록 따라야 하는 프로토콜을 뜻합니다.
서비스는 일반적으로 타사 또는 레거시의 유용한 클래스를 뜻합니다. 클라이언트는 서비스 클래스를 직접 사용할 수 없습니다. 왜냐하면 서비스 클래스는 호환되지 않는 인터페이스를 가지고 있기 때문입니다.
어댑터는 클라이언트와 서비스 양쪽에서 작동할 수 있는 클래스로, 서비스 객체를 래핑하는 동안 클라이언트 인터페이스를 구현합니다. 어댑터는 어댑터 인터페이스를 통해 클라이언트로부터 호출들을 수신한 후 이 호출을 래핑된 서비스 객체가 이해할 수 있는 형식의 호출들로 변환합니다.
클라이언트 코드는 클라이언트 인터페이스를 통해 어댑터와 작동하는 한 구상 어댑터 클래스와 결합하지 않습니다. 덕분에 기존 클라이언트 코드를 손상하지 않고 새로운 유형의 어댑터들을 프로그램에 도입할 수 있습니다. 이것은 서비스 클래스의 인터페이스가 변경되거나 교체될 때 유용할 수 있습니다: 클라이언트 코드를 변경하지 않은 채 새 어댑터 클래스를 생성할 수 있으니까요.
클래스 어댑터( 다중 상속을 지원하는 프로그래밍 언어에서만 구현 가능) 이 구현은 상속을 사용하며, 어댑터는 동시에 두 객체의 인터페이스를 상속합니다. 이 방식은 C++ 와 같이 다중 상속을 지원하는 프로그래밍 언어에서만 구현할 수 있습니다.
클래스 어댑터는 객체를 래핑할 필요가 없습니다. 그 이유는 클라이언트와 서비스 양쪽에서 행동들을 상속받기 때문입니다. 위의 어댑테이션(적용)은 오버라이딩된 메서드 내에서 발생합니다. 위 어댑터는 기존 클라이언트 클래스 대신 사용할 수 있습니다.
예시
어댑터는 정사각형 지름의 절반(즉, 사각형 못을 수용할 수 있는 가장 작은 원의 반지름)을 반지름으로 가진 둥근 못인 척 합니다.
결국 RoundHole에서 fits(RoundPeg roundPeg)함수를 처리해야한다. 이는 매개변수로 RoundPeg 타입이 필요하다.
SquarePeg가 RoundPeg타입으로 작용할려면 RoundPeg를 상속받은 SquarePegAdapter를 사용하여, fits()함수의 매개변수로 활용될 수 있다.
1 2 3 4 5 6 7 8 9
SquarePeg smallSqPeg = new SquarePeg(2); // hole.fits(smallSqPeg); // Won't compile.
// Adapter solves the problem. SquarePegAdapter smallSqPegAdapter = new SquarePegAdapter(smallSqPeg) if (hole.fits(smallSqPegAdapter)) { System.out.println("Square peg w2 fits round hole r5."); }
/** * Adapter allows fitting square pegs into round holes. * 어댑터는 정사각형 지름의 절반(즉, 사각형 못을 수용할 수 있는 가장 작은 원의 반지름)을 반지름으로 가진 둥근 못인 척 합니다. */ publicclassSquarePegAdapterextendsRoundPeg{ private SquarePeg peg;
@Override publicdoublegetRadius(){ double result; // Calculate a minimum circle radius, which can fit this peg. result = (Math.sqrt(Math.pow((peg.getWidth() / 2), 2) * 2)); return result; } }
@GetMapping("/designpattern6") @ResponseBody public HttpStatus designpattern6(HttpServletRequest request){ System.out.println(request.getRequestURL());
// Round fits round, no surprise. RoundHole hole = new RoundHole(5); RoundPeg rpeg = new RoundPeg(5); if (hole.fits(rpeg)) { System.out.println("Round peg r5 fits round hole r5."); }
SquarePeg smallSqPeg = new SquarePeg(2); SquarePeg largeSqPeg = new SquarePeg(20); // hole.fits(smallSqPeg); // Won't compile.
// Adapter solves the problem. SquarePegAdapter smallSqPegAdapter = new SquarePegAdapter(smallSqPeg); SquarePegAdapter largeSqPegAdapter = new SquarePegAdapter(largeSqPeg); if (hole.fits(smallSqPegAdapter)) { System.out.println("Square peg w2 fits round hole r5."); } if (!hole.fits(largeSqPegAdapter)) { System.out.println("Square peg w20 does not fit into round hole r5."); }
return HttpStatus.OK; }
적용
어댑터 클래스는 기존 클래스를 사용하고 싶지만 그 인터페이스가 나머지 코드와 호환되지 않을 때 사용하세요.
어댑터 패턴은 당신의 코드와 레거시 클래스, 타사 클래스 또는 특이한 인터페이스가 있는 다른 클래스 간의 변환기 역할을 하는 중간 레이어 클래스를 만들 수 있도록 합니다.
이 패턴은 부모 클래스에 추가할 수 없는 어떤 공통 기능들이 없는 여러 기존 자식 클래스들을 재사용하려는 경우에 사용하세요.
각 자식 클래스를 확장한 후 누락된 기능들을 새 자식 클래스들에 넣을 수 있습니다. 하지만 해당 코드를 모든 새 클래스들에 복제해야 하며, 그건 정말 나쁜 냄새가 나는 코드일 것입니다.
이보다 훨씬 더 깔끔한 해결책은 누락된 기능을 어댑터 클래스에 넣는 것입니다. 그 후 어댑터 내부에 누락된 기능이 있는 객체들을 래핑하면 필요한 기능들을 동적으로 얻을 것입니다. 이 해결책이 작동하려면 대상 클래스들에는 반드시 공통 인터페이스가 있어야 하며 어댑터의 필드는 해당 인터페이스를 따라야 합니다. 위 접근 방식은 데코레이터 패턴과 매우 유사합니다.
장단점
장점
단일 책임 원칙. 프로그램의 기본 비즈니스 로직에서 인터페이스 또는 데이터 변환 코드를 분리할 수 있습니다.
개방/폐쇄 원칙. 클라이언트 코드가 클라이언트 인터페이스를 통해 어댑터와 작동하는 한, 기존의 클라이언트 코드를 손상시키지 않고 새로운 유형의 어댑터들을 프로그램에 도입할 수 있습니다.
단점
다수의 새로운 인터페이스와 클래스들을 도입해야 하므로 코드의 전반적인 복잡성이 증가합니다. 때로는 코드의 나머지 부분과 작동하도록 서비스 클래스를 변경하는 것이 더 간단합니다.
브리지 패턴
의도
브리지는 큰 클래스 또는 밀접하게 관련된 클래스들의 집합을 두 개의 개별 계층구조(추상화 및 구현)로 나눈 후 각각 독립적으로 개발할 수 있도록 하는 구조 디자인 패턴입니다.
즉, 추상화한후, 객체 생성시 관련 의존하는 객체도 생성자에서 결정하게함.
구조
추상화는 상위 수준의 제어 논리를 제공하며, 구현 객체에 의존해 실제 하위 수준 작업들을 수행합니다.
구현은 모든 구상 구현들에 공통적인 인터페이스를 선언하며, 추상화는 여기에 선언된 메서드들을 통해서만 구현 객체와 소통할 수 있습니다.
추상화는 구현과 같은 메서드들을 나열할 수 있지만 보통은 구현이 선언한 다양한 원시 작업들에 의존하는 몇 가지 복잡한 행동들을 선언합니다.
구상 구현들에는 플랫폼별 맞춤형 코드가 포함됩니다.
정제된 추상화들은 제어 논리의 변형들을 제공합니다. 그들은 그들의 부모처럼 일반 구현 인터페이스를 통해 다른 구현들과 작업합니다.
일반적으로 클라이언트는 추상화와 작업하는데만 관심이 있습니다. 그러나 추상화 객체를 구현 객체들 중 하나와 연결하는 것도 클라이언트의 역할입니다.
예시
리모콘과 디바이스
기초 리모컨 클래스는 이 클래스를 장치 객체와 연결하는 참조 필드를 선언합니다. 모든 리모컨은 일반 장치 인터페이스를 통해 장치들과 작동하므로 같은 리모컨이 여러 장치 유형을 지원할 수 있습니다.
장치 클래스들과 독립적으로 리모컨 클래스들을 개발할 수 있으며, 필요한 것은 새로운 리모컨 자식 클래스를 만드는 것뿐입니다. 예를 들어 기초 리모컨에는 버튼이 두 개뿐일 수 있지만, 추가 터치스크린과 추가 배터리 같은 기능들도 가지도록 확장할 수 있습니다.
클라이언트 코드는 Remote의 생성자를 통해 원하는 유형의 리모컨을 특정 장치 객체와 연결합니다.
Tests with basic remote. Remote: power toggle ------------------------------------ | I'm TV set. | I'm enabled | Current volume is 30% | Current channel is 1 ------------------------------------
Tests with advanced remote. Remote: power toggle Remote: mute ------------------------------------ | I'm TV set. | I'm disabled | Current volume is 0% | Current channel is 1 ------------------------------------
Tests with basic remote. Remote: power toggle ------------------------------------ | I'm radio. | I'm enabled | Current volume is 30% | Current channel is 1 ------------------------------------
Tests with advanced remote. Remote: power toggle Remote: mute ------------------------------------ | I'm radio. | I'm disabled | Current volume is 0% | Current channel is 1 ------------------------------------
적용
브리지 패턴은 당신이 어떤 기능의 여러 변형을 가진 모놀리식 클래스를 나누고 정돈하려 할 때 사용하세요. (예: 클래스가 다양한 데이터베이스 서버들과 작동할 수 있는 경우).
클래스가 성장할수록 그 작동 방식을 파악하기가 더 어려워지고 해당 클래스를 변경하는 데 더더욱 오랜 시간이 걸립니다. 클래스 기능의 여러 변형 중 하나를 변경하려면 클래스 전체에 걸쳐 여러 가지 변경을 수행해야 할 수 있으며, 이를 수행 중 개발자들은 종종 실수하거나 일부 중요한 부작용들을 해결하지 않기도 합니다.
브리지 패턴을 사용하면 모놀리식 클래스를 여러 클래스 계층구조로 나눌 수 있습니다. 그런 다음 각 계층구조의 클래스들을 다른 계층구조들에 있는 클래스들과는 독립적으로 변경할 수 있습니다. 이 접근 방식은 코드의 유지관리를 단순화하고 기존 코드가 손상될 위험을 최소화합니다.
이 패턴은 여러 직교(독립) 차원에서 클래스를 확장해야 할 때 사용하세요.
브리지 패턴은 각 차원에 대해 별도의 클래스 계층구조를 추출할 것을 제안합니다. 원래 클래스는 모든 작업을 자체적으로 수행하는 대신 추출된 계층구조들에 속한 객체들에 관련 작업들을 위임합니다.
브리지 패턴은 런타임(실행시간)에 구현을 전환할 수 있어야 할 때에 사용하세요.
선택 사항이지만 브리지 패턴을 사용하면 추상화 내부의 구현 객체를 바꿀 수 있으며, 그렇게 하려면 필드에 새 값을 할당하기만 하면 됩니다.
위 항목은 많은 사람이 브리지와 전략 패턴을 혼동하는 주된 이유입니다. 패턴은 클래스의 구조를 설계하는 특정 방법 그 이상의 것이라는 것을 기억하세요. 패턴은 제기되고 있는 문제 및 의도에 대하여도 소통할 수 있습니다.
장단점
장점
플랫폼 독립적인 클래스들과 앱들을 만들 수 있습니다.
클라이언트 코드는 상위 수준의 추상화를 통해 작동하며, 플랫폼 세부 정보에 노출되지 않습니다.
개방/폐쇄 원칙. 새로운 추상화들과 구현들을 상호 독립적으로 도입할 수 있습니다.
단일 책임 원칙. 추상화의 상위 수준 논리와 구현의 플랫폼 세부 정보에 집중할 수 있습니다.
단점
결합도가 높은 클래스에 패턴을 적용하여 코드를 더 복잡하게 만들 수 있습니다.
복합체 패턴
의도
복합체 패턴은 객체들을 트리 구조들로 구성한 후, 이러한 구조들과 개별 객체들처럼 작업할 수 있도록 하는 구조 패턴입니다.
구조
컴포넌트 인터페이스는 트리의 단순 요소들과 복잡한 요소들 모두에 공통적인 작업을 설명합니다.(잎, 컨테이너 둘다 가져야하는 공통적인 작업)
잎은 트리의 기본 요소이며 하위요소가 없습니다.
일반적으로 잎 컴포넌트들은 작업을 위임할 하위요소가 없어서 대부분의 실제 작업들을 수행합니다.
컨테이너(일명 복합체)는 하위 요소들(잎 또는 기타 컨테이너)이 있는 요소입니다. 컨테이너는 자녀들의 구상 클래스들을 알지 못하며, 컴포넌트 인터페이스를 통해서만 모든 하위 요소들과 함께 작동합니다.
요청을 전달받으면 컨테이너는 작업을 하위 요소들에 위임하고 중간 결과들을 처리한 다음 최종 결과들을 클라이언트에 반환합니다.
클라이언트는 컴포넌트 인터페이스를 통해 모든 요소들과 작동합니다. 그 결과 클라이언트는 트리의 단순 요소들 또는 복잡한 요소들 모두에 대해 같은 방식으로 작업할 수 있습니다.
@Override publicvoidoperation(){ System.out.println(this + " 호출"); // 내부 리스트를 순회하여, 단일 Leaf이면 값을 출력하고, // 또다른 서브 복합 객체이면, 다시 그 내부를 순회하는 재귀 함수 동작이 된다. for (Component component : components) { component.operation(); // 자기 자신을 호출(재귀) } } public List<Component> getChild(){ return components; } }
- Input ---------------- Name,Salary John Smith,100000 Steven Jobs,912000 - Encoded -------------- Zkt7e1Q5eU8yUm1Qe0ZsdHJ2VXp6dDBKVnhrUHtUe0sxRUYxQkJIdjVLTVZ0dVI5Q2IwOXFISmVUMU5rcENCQmdxRlByaD4+ - Decoded -------------- Name,Salary John Smith,100000 Steven Jobs,912000
적용
데코레이터 패턴은 이 객체들을 사용하는 코드를 훼손하지 않으면서 런타임에 추가 행동들을 객체들에 할당할 수 있어야 할 때 사용하세요.
데코레이터는 비즈니스 로직을 계층으로 구성하고, 각 계층에 데코레이터를 생성하고 런타임에 이 로직의 다양한 조합들로 객체들을 구성할 수 있도록 합니다. 이러한 모든 객체가 공통 인터페이스를 따르기 때문에 클라이언트 코드는 해당 모든 객체를 같은 방식으로 다룰 수 있습니다.
이 패턴은 상속을 사용하여 객체의 행동을 확장하는 것이 어색하거나 불가능할 때 사용하세요.
많은 프로그래밍 언어에는 클래스의 추가 확장을 방지하는 데 사용할 수 있는 final 키워드가 있습니다. Final 클래스의 경우 기존 행동들을 재사용할 수 있는 유일한 방법은 데코레이터 패턴을 사용하여 클래스를 자체 래퍼로 래핑하는 것입니다.
장단점
장점
새 자식 클래스를 만들지 않고도 객체의 행동을 확장할 수 있습니다.
런타임에 객체들에서부터 책임들을 추가하거나 제거할 수 있습니다.
객체를 여러 데코레이터로 래핑하여 여러 행동들을 합성할 수 있습니다.
단일 책임 원칙. 다양한 행동들의 여러 변형들을 구현하는 모놀리식 클래스를 여러 개의 작은 클래스들로 나눌 수 있습니다.
단점
래퍼들의 스택에서 특정 래퍼를 제거하기가 어렵습니다.
데코레이터의 행동이 데코레이터 스택 내의 순서에 의존하지 않는 방식으로 데코레이터를 구현하기가 어렵습니다.
계층들의 초기 설정 코드가 보기 흉할 수 있습니다.
퍼사드 패턴
의도
퍼사드 패턴은 라이브러리에 대한, 프레임워크에 대한 또는 다른 클래스들의 복잡한 집합에 대한 단순화된 인터페이스를 제공하는 구조적 디자인 패턴입니다.
구조
퍼사드 패턴을 사용하면 하위 시스템 기능들의 특정 부분에 편리하게 접근할 수 있습니다. 또 퍼사드는 클라이언트의 요청을 어디로 보내야 하는지와 움직이는 모든 부품을 어떻게 작동해야 하는지를 알고 있습니다.
추가적인 퍼사드 클래스를 생성하여 하나의 퍼사드를 관련 없는 기능들로 오염시켜 복잡한 구조로 만드는 것을 방지할 수 있습니다. 추가 퍼사드들은 클라이언트들과 다른 퍼사드들 모두에 사용할 수 있습니다.
복잡한 하위 시스템은 수십 개의 다양한 객체들로 구성됩니다. 이 모든 객체가 의미 있는 작업을 수행하도록 하려면, 하위 시스템의 세부적인 구현 정보를 깊이 있게 살펴야 합니다. 예를 들어 올바른 순서로 객체들을 초기화하고 그들에게 적절한 형식의 데이터를 제공하는 등의 작업을 수행해야 합니다.
하위 시스템 클래스들은 퍼사드의 존재를 인식하지 못합니다. 이들은 시스템 내에서 작동하며, 매개체 없이 직접 서로와 작업합니다.
퍼사드 패턴은 당신이 복잡한 하위 시스템에 대한 제한적이지만 간단한 인터페이스가 필요할 때 사용하세요.
하위 시스템은 시간이 지날수록 더 복잡해지곤 합니다. 디자인 패턴들을 적용하더라도 보통은 생성되는 클래스들이 점점 더 많아지게 됩니다. 하위 시스템은 더 유연해지고 더 많은 다양한 상황에서 재사용할 수 있도록 변경될 수도 있지만, 해당 시스템이 클라이언트에게 요구하는 설정 및 상용구 코드의 양은 점점 더 많아질 것입니다. 이 문제를 해결하기 위해 퍼사드는 대부분의 클라이언트 요건에 부합하면서 하위 시스템에서 가장 많이 사용되는 기능들로 향하는 지름길을 제공합니다.
퍼사드 패턴은 하위 시스템을 계층들로 구성하려는 경우 사용하세요.
하위시스템의 각 계층에 대한 진입점을 정의하기 위해 퍼사드 패턴들을 생성하세요. 당신은 여러 하위시스템이 퍼사드 패턴들을 통해서만 통신하도록 함으로써 해당 하위시스템 간의 결합도를 줄일 수 있습니다.
예를 들어 당신의 비디오 변환 프레임워크는 비디오 또는 오디오 관련의 두 가지 계층으로 나뉠 수 있습니다. 각 계층에 대해 퍼사드를 만든 다음 각 계층의 클래스들이 해당 퍼사드들을 통해 서로 통신하도록 할 수 있습니다. 이러한 접근 방식은 중재자 패턴과 매우 유사합니다.
장단점
장점
복잡한 하위 시스템에서 코드를 별도로 분리할 수 있습니다.
단점
퍼사드는 앱의 모든 클래스에 결합된 전지전능한 객체가 될 수 있습니다.
플라이웨이트
의도
플라이웨이트는 각 객체에 모든 데이터를 유지하는 대신 여러 객체들 간에 상태의 공통 부분들을 공유하여 사용할 수 있는 RAM에 더 많은 객체들을 포함할 수 있도록 하는 구조 디자인 패턴입니다.
구조
플라이웨이트 패턴은 단지 최적화에 불과합니다. 이 패턴을 적용하기 전에 프로그램이 동시에 메모리에 유사한 객체들을 대량으로 보유하는 것과 관련된 RAM 소비 문제가 있는지 확인하시고 이 문제가 다른 의미 있는 방법으로 해결될 수 없는지도 확인하세요.
플라이웨이트 클래스에는 여러 객체들 간에 공유할 수 있는 원래 객체의 상태의 부분이 포함됩니다. 같은 플라이웨이트 객체가 다양한 콘텍스트에서 사용될 수 있습니다. 플라이웨이트 내부에 저장된 상태를 고유한(intrinsic) 상태라고 하며, 플라이웨이트의 메서드에 전달된 상태를 공유한(extrinsic) 상태라고 합니다.
콘텍스트 클래스는 공유한 상태를 포함하며, 이 상태는 모든 원본 객체들에서 고유합니다. 콘텍스트가 플라이웨이트 객체 중 하나와 쌍을 이루면 원래 객체의 전체 상태를 나타냅니다.
일반적으로 원래 객체의 행동은 플라이웨이트 클래스에 남아 있습니다. 이 경우 플라이웨이트의 메서드의 호출자는 공유한 상태의 적절한 부분들을 메서드의 매개변수들에 전달해야 합니다. 반면에, 행동은 콘텍스트 클래스로 이동할 수 있으며, 이 클래스는 연결된 플라이웨이트를 단순히 데이터 객체로 사용할 것입니다.
클라이언트는 플라이웨이트들의 공유된 상태를 저장하거나 계산합니다. 클라이언트의 관점에서 플라이웨이트는 일부 콘텍스트 데이터를 그의 메서드들의 매개변수들에 전달하여 런타임에 설정될 수 있는 템플릿 객체입니다.
플라이웨이트 팩토리는 기존 플라이웨이트들의 풀을 관리합니다. 이 팩토리로 인해 클라이언트들은 플라이웨이트들을 직접 만들지 않는 대신 원하는 플라이웨이트의 고유한 상태의 일부를 전달하여 공장을 호출합니다. 팩토리는 이전에 생성된 플라이웨이트들을 살펴보고 검색 기준과 일치하는 기존 플라이웨이트를 반환하거나 기준에 맞는 플라이웨이트가 발견되지 않으면 새로 생성합니다.
publicvoiddraw(Graphics g, int x, int y){ g.setColor(Color.BLACK); g.fillRect(x - 1, y, 3, 5); g.setColor(color); g.fillOval(x - 5, y - 10, 10, 10); } }
publicclassTreeFactory{ static Map<String, TreeType> treeTypes = new HashMap<>();
publicstatic TreeType getTreeType(String name, Color color, String otherTreeData){ TreeType result = treeTypes.get(name); if (result == null) { result = new TreeType(name, color, otherTreeData); treeTypes.put(name, result); } return result; } }
publicclassForestextendsJFrame{ private List<Tree> trees = new ArrayList<>();
publicvoidplantTree(int x, int y, String name, Color color, String otherTreeData){ TreeType type = TreeFactory.getTreeType(name, color, otherTreeData); Tree tree = new Tree(x, y, type); trees.add(tree); }
@Override publicvoidpaint(Graphics graphics){ for (Tree tree : trees) { tree.draw(graphics); } } }
experienceNetworkLatency(); HashMap<String, Video> hmap = new HashMap<String, Video>(); hmap.put("catzzzzzzzzz", new Video("sadgahasgdas", "Catzzzz.avi")); hmap.put("mkafksangasj", new Video("mkafksangasj", "Dog play with ball.mp4")); hmap.put("dancesvideoo", new Video("asdfas3ffasd", "Dancing video.mpq")); hmap.put("dlsdk5jfslaf", new Video("dlsdk5jfslaf", "Barcelona vs RealM.mov")); hmap.put("3sdfgsd1j333", new Video("3sdfgsd1j333", "Programing lesson#1.avi"));
System.out.print("Done!" + "\n"); return hmap; }
private Video getSomeVideo(String videoId){ System.out.print("Downloading video... ");
experienceNetworkLatency(); Video video = new Video(videoId, "Some video title");
System.out.print("Done!" + "\n"); return video; }
}
publicclassVideo{ public String id; public String title; public String data;
publicclassYouTubeCacheProxyimplementsThirdPartyYouTubeLib{ private ThirdPartyYouTubeLib youtubeService; private HashMap<String, Video> cachePopular = new HashMap<String, Video>(); private HashMap<String, Video> cacheAll = new HashMap<String, Video>();
publicYouTubeCacheProxy(){ this.youtubeService = new ThirdPartyYouTubeClass(); }
@Override public HashMap<String, Video> popularVideos(){ if (cachePopular.isEmpty()) { cachePopular = youtubeService.popularVideos(); } else { System.out.println("Retrieved list from cache."); } return cachePopular; }
@Override public Video getVideo(String videoId){ Video video = cacheAll.get(videoId); if (video == null) { video = youtubeService.getVideo(videoId); cacheAll.put(videoId, video); } else { System.out.println("Retrieved video '" + videoId + "' from cache."); } return video; }
Connecting to http://www.youtube.com... Connected! Downloading populars... Done!
------------------------------- Most popular videos on YouTube (imagine fancy HTML) ID: sadgahasgdas / Title: Catzzzz.avi ID: asdfas3ffasd / Title: Dancing video.mpq ID: 3sdfgsd1j333 / Title: Programing lesson#1.avi ID: mkafksangasj / Title: Dog play with ball.mp4 ID: dlsdk5jfslaf / Title: Barcelona vs RealM.mov -------------------------------
Connecting to http://www.youtube.com/catzzzzzzzzz... Connected! Downloading video... Done!
------------------------------- Video page (imagine fancy HTML) ID: catzzzzzzzzz Title: Some video title Video: Random video. -------------------------------
적용
지연된 초기화(가상 프록시). 이것은 어쩌다 필요한 무거운 서비스 객체가 항상 가동되어 있어 시스템 자원들을 낭비하는 경우입니다.
앱이 시작될 때 객체를 생성하는 대신, 객체 초기화를 실제로 초기화가 필요한 시점까지 지연할 수 있습니다.
접근 제어 (보호 프록시). 당신이 특정 클라이언트들만 서비스 객체를 사용할 수 있도록 하려는 경우에 사용할 수 있습니다. 예를 들어 당신의 객체들이 운영 체제의 중요한 부분이고 클라이언트들이 다양한 실행된 응용 프로그램(악의적인 응용 프로그램 포함)인 경우입니다.
이 프록시는 클라이언트의 자격 증명이 어떤 정해진 기준과 일치하는 경우에만 서비스 객체에 요청을 전달할 수 있습니다.
원격 서비스의 로컬 실행 (원격 프록시). 서비스 객체가 원격 서버에 있는 경우입니다.
이 경우 프록시는 네트워크를 통해 클라이언트 요청을 전달하여 네트워크와의 작업의 모든 복잡한 세부 사항을 처리합니다.
요청들의 로깅(로깅 프록시). 서비스 객체에 대한 요청들의 기록을 유지하려는 경우입니다.
프록시는 각 요청을 서비스에 전달하기 전에 로깅(기록)할 수 있습니다.
요청 결과들의 캐싱(캐싱 프록시). 이것은 클라이언트 요청들의 결과들을 캐시하고 이 캐시들의 수명 주기를 관리해야 할 때, 특히 결과들이 상당히 큰 경우에 사용됩니다.
프록시는 항상 같은 결과를 생성하는 반복 요청들에 대해 캐싱을 구현할 수 있습니다. 프록시는 요청들의 매개변수들을 캐시 키들로 사용할 수 있습니다.
스마트 참조. 이것은 사용하는 클라이언트들이 없어 거대한 객체를 해제할 수 있어야 할 때 사용됩니다.
프록시는 서비스 객체 또는 그 결과에 대한 참조를 얻은 클라이언트들을 추적할 수 있습니다. 때때로 프록시는 클라이언트들을 점검하여 클라이언트들이 여전히 활성 상태인지를 확인할 수 있습니다. 클라이언트 리스트가 비어 있으면 프록시는 해당 서비스 객체를 닫고 그에 해당하는 시스템 자원을 확보할 수 있습니다.
또 프록시는 클라이언트가 서비스 객체를 수정했는지도 추적할 수 있으며, 변경되지 않은 객체는 다른 클라이언트들이 재사용할 수 있습니다.
장단점
장점
클라이언트들이 알지 못하는 상태에서 서비스 객체를 제어할 수 있습니다.
클라이언트들이 신경 쓰지 않을 때 서비스 객체의 수명 주기를 관리할 수 있습니다.
프록시는 서비스 객체가 준비되지 않았거나 사용할 수 없는 경우에도 작동합니다.
개방/폐쇄 원칙. 서비스나 클라이언트들을 변경하지 않고도 새 프록시들을 도입할 수 있습니다.