Writing /volume1/Web/Public/dokuwiki/data/log/deprecated/2024-11-15.log failed
差分
このページの2つのバージョン間の差分を表示します。
次のリビジョン | 前のリビジョン | ||
study:java:design_pattern:proxy [2008/10/13 13:12] – created banana | study:java:design_pattern:proxy [2021/05/12 00:27] (現在) – [ImageProxy Class] banana | ||
---|---|---|---|
行 2: | 行 2: | ||
원격 프록시(Remote Proxy)는 일반적인 프록시 패턴(Proxy Pattern)을 구현하는 방법 가운데 하나입니다. | 원격 프록시(Remote Proxy)는 일반적인 프록시 패턴(Proxy Pattern)을 구현하는 방법 가운데 하나입니다. | ||
이 외에도 몇 가지 변형된 방법이 있는데, 잠시 후에 알아보도록 하겠습니다. 일단 지금은 일반 프록시 패턴 | 이 외에도 몇 가지 변형된 방법이 있는데, 잠시 후에 알아보도록 하겠습니다. 일단 지금은 일반 프록시 패턴 | ||
- | 에 대해서 살표보도록 하겠습니다. | + | 에 대해서 살펴보도록 하겠습니다. |
프록시 패턴은 다음과 같이 정의됩니다. | 프록시 패턴은 다음과 같이 정의됩니다. | ||
行 9: | 行 9: | ||
대리인이나 대변인에 해당하는 객체를 제공하는 패턴 | 대리인이나 대변인에 해당하는 객체를 제공하는 패턴 | ||
</ | </ | ||
+ | |||
+ | 프록시는 다른 객체에 대한 대변자라고 봐도 무방합니다. 하지만 접근을 제어하는 프록시는 어떤 것일까요? | ||
+ | 이상하게 들릴수도 있겠지만, | ||
+ | 대한 접근을 제어하고 있다고 생각하면 되거든요. 클라이언트인 모니터링용 객체에서 원격 객체하고 직접 데이터를 | ||
+ | 주고 받을 수 없었기 때문에 프록시에서 접근을 제어해 줘야 했습니다. 따라서 어떤 면에서 보면 원격 프록시가 | ||
+ | 접근을 제어해서 네트워크 관련 사항을 챙겨 줬다고 할 수도 있는 거죠. 프록시 패턴에는 수많은 변종이 있습니다. | ||
+ | 그리고 그러한 변종들은 대개 프록시에서 접근을 제어하는 방법면에서 차이를 보입니다. 자세한 내용은 나중에 알 | ||
+ | 아보기로 하고, 일단 프록시에서 접근을 제어하는 몇가지 방법을 나열해 보면 다음과 같습니다. | ||
+ | |||
+ | * 원격 프록시를 써서 원격 객체에 대한 접근을 제어할 수 있습니다. | ||
+ | * 가상 프록시(virtual proxy)를 써서 생성하기 힘든 자원에 대한 접근을 제어할 수 있습니다. | ||
+ | * 보호 프록시(protection proxy)를 써서 접근 권한이 필요한 자원에 대한 접근을 제어할 수 있습니다. | ||
+ | |||
+ | 프록시 패턴의 정의에 대해서는 이 정도면 된 것 같으니까 클래스 다이어그램을 살펴볼까요? | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== Class Diagram ===== | ||
+ | {{: | ||
+ | |||
+ | **다이어그램을 훑어볼까요? | ||
+ | |||
+ | 우선 %%RealSubject%%와 Proxy의 인터페이스를 제공하는 Subject 인터페이스가 있습니다. | ||
+ | 두 객체에서 똑같은 인터페이스를 구현하기 때문에 %%RealSubject%%가 들어가야 할 자리에 | ||
+ | Proxy를 대신 집어넣을 수 있습니다. | ||
+ | |||
+ | 실제 작업은 **%%RealSubject%%** 객체에서 처리됩니다. **Proxy**는 이 객체의 대변인 역할을 하면서 | ||
+ | 이 객체에 대한 접근을 제어하죠. | ||
+ | |||
+ | **Proxy**에는 **%%RealSubject%%**에 대한 레퍼런스가 들어있습니다. **Proxy**에서 **%%RealSubject%%**를 생성하거나 | ||
+ | 제거하는 역할을 책임지는 경우도 있습니다.클라이언트는 항상 **Proxy**를 통해서 **%%RealSubject%%**하고 데이터를 | ||
+ | 주고 받습니다. **Proxy**와 **%%RealSubject%%**는 똑같은 인터페이스(Subject)를 구현하기 때문에 **%%RealSubject%%** | ||
+ | | ||
+ | 접근을 제어하는 역할도 맡게 됩니다. **%%RealSubject%%**가 원격 시스템에서 돌아가거나, | ||
+ | 비용이 많이 들거나, 어떤 방식으로든지 **%%RealSubject%%**에 대한 접근이 통제되어 있는 경우에 접근을 제어하는 | ||
+ | 객체가 필요할 수 있습니다. | ||
+ | |||
+ | 지금까지 일반적인 프록시 패턴에 대해서 살펴봤습니다. 이제 프록시를 활용하는 다른 방법을 살펴보도록 하죠. | ||
+ | |||
+ | ===== Virtual Proxy ===== | ||
+ | 방금 전에 프록시 패턴의 정의에 대해서 배웠습니다. 그리고 그 전에 살펴본 예제는 프록시 패턴 중이 하나인 | ||
+ | 원격 프록시(Remote Proxy)를 사용하는 예제였죠. 이제 가상 프록시(Virtual Proxy)라는 다른 형식의 | ||
+ | 프록시에 대해 살펴보겠습니다. 앞으로 계속해서 느낄 수 있겠지만, | ||
+ | 하지만 어떤 형태로 사용하든 결국은 기본 프록시 디자인을 따르게 되죠. 어떻게 그렇게 다양한 형태로 활용될 | ||
+ | 수 있는 걸까요? 그 이유는 프록시 패턴은 여러 가지 다양한 상황에 적용될 수 있기 때문입니다. | ||
+ | |||
+ | 그럼 이제 가상 프록시의 예제를 살펴보도록 할까요? | ||
+ | |||
+ | ===== CD Cover Viewer ===== | ||
+ | CD 커버를 보여주는 뷰어를 만들기로 했다고 가정해 봅시다. CD 타이틀 메뉴를 만든 다음 이미지를 아마존 닷 컴 | ||
+ | 같은 온라인 서비스로부터 가져오면 꽤 편리할 것입니다. 스윙을 사용한다면 아이콘을 만든 다음, 그 아이콘 객체 | ||
+ | 로 하여금 네트워크를 통해서 이미지를 불러오도록 할 수 있을 것입니다. 그런데, 이런 방법을 사용하면 네트워크의 | ||
+ | 상태와 인터넷 속도에 따라서 CD 커버 이미지를 가져오는데 약간 시간이 걸릴 수 있기 때문에, 이미지를 불러오는 | ||
+ | 동안 화면에 뭔가 다른 걸 보여주는 게 좋습니다. 그리고 이미지를 기다리는 동안 애플리케이션 전체가 작동을 멈추 | ||
+ | 는 것도 바람직하지 않죠. | ||
+ | |||
+ | 가상 프록시를 사용하면 이런 문제를 간단하게 해결할 수 있습니다. 가상 프록시가 아이콘 대신 백그라운드에서 | ||
+ | 이미지를 불러오는 작업을 처리하고, | ||
+ | 같은 메세지를 보여주면 되니까요. 일단 이미지 로딩이 끝나고 나면 프록시에서는 아이콘 객체한테 모든 작업을 | ||
+ | 맡기면 됩니다. | ||
+ | |||
+ | |||
+ | ===== Design of CD Cover viewer ===== | ||
+ | CD 커버 뷰어 코드를 작성하기 전에 클래스 다이어그램을 살펴봅시다. 전에 만들었던 원격 프록시 클래스 다이어그램하고 | ||
+ | 똑같이 생겼지만, | ||
+ | 생성하는 데 많은 비용이 필요한 객체(아이콘용 데이터를 네트워크를 통해서 가져와야 하기 때문에 시간이 많이 걸릴 수 | ||
+ | 있죠)를 숨기기 위한 용도로 쓰입니다. | ||
+ | |||
+ | {{: | ||
+ | |||
+ | **%%ImageProxy%% 작동 방법** | ||
+ | |||
+ | - **%%ImageProxy%%에서는 우선 %%ImageIcon%%을 생성하고 네트워크 URL로부터 이미지를 불러옵니다.** | ||
+ | - **이미지를 가져오는 동안에는 " | ||
+ | - **이미지 로딩이 끝나면 paintIcon(), | ||
+ | - **사용자가 새로운 이미지를 요청하면 프록시를 새로 만들고 위의 과정을 새로 진행합니다.** | ||
+ | |||
+ | |||
+ | |||
+ | ===== ImageProxy Class ===== | ||
+ | <code java> | ||
+ | /** | ||
+ | * 네트워크를 통해서 이미지를 가져오는 동안 대신 실제객체를 대신하는 Proxy클래스입니다.< | ||
+ | * imageIcon의 인스턴스가 만들어진 후에 화면을 표시하게 되면 paintIcon()메소드가 호출되면서 로딩중이라는 | ||
+ | * 메시지가 아닌 실제 이미지가 화면에 표시됩니다. | ||
+ | | ||
+ | * @author Administrator | ||
+ | * | ||
+ | */ | ||
+ | public class ImageProxy implements Icon { | ||
+ | ImageIcon imageIcon; | ||
+ | URL imageURL; | ||
+ | Thread retrievalThread; | ||
+ | boolean retrieving = false; | ||
+ | |||
+ | |||
+ | public ImageProxy(URL imageURL) { | ||
+ | super(); | ||
+ | this.imageURL = imageURL; | ||
+ | } | ||
+ | |||
+ | /* (non-Javadoc) | ||
+ | * @see javax.swing.Icon# | ||
+ | */ | ||
+ | public int getIconHeight() { | ||
+ | if (imageIcon != null) return imageIcon.getIconHeight(); | ||
+ | else return 600; | ||
+ | } | ||
+ | |||
+ | /* (non-Javadoc) | ||
+ | * @see javax.swing.Icon# | ||
+ | */ | ||
+ | public int getIconWidth() { | ||
+ | if (imageIcon != null) return imageIcon.getIconWidth(); | ||
+ | else return 800; | ||
+ | } | ||
+ | |||
+ | /* (non-Javadoc) | ||
+ | * @see javax.swing.Icon# | ||
+ | */ | ||
+ | public void paintIcon(final Component c, Graphics g, int x, int y) { | ||
+ | if (imageIcon != null) { | ||
+ | // | ||
+ | imageIcon.paintIcon(c, | ||
+ | } else { | ||
+ | g.drawString(" | ||
+ | // | ||
+ | if (!retrieving) { | ||
+ | // | ||
+ | //할 수 있습니다. | ||
+ | retrieving = true; | ||
+ | |||
+ | // | ||
+ | retrievalThread = new Thread(new Runnable(){ | ||
+ | public void run(){ | ||
+ | try { | ||
+ | // | ||
+ | imageIcon = new ImageIcon(imageURL, | ||
+ | // | ||
+ | c.repaint(); | ||
+ | } catch (Exception e) { | ||
+ | e.printStackTrace(); | ||
+ | } | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | retrievalThread.start(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== ImageComponent Class ===== | ||
+ | <code java> | ||
+ | /** | ||
+ | * @author Administrator | ||
+ | * | ||
+ | */ | ||
+ | public class ImageComponent extends JComponent { | ||
+ | private Icon icon; | ||
+ | |||
+ | public ImageComponent(Icon icon) { | ||
+ | super(); | ||
+ | this.icon = icon; | ||
+ | } | ||
+ | |||
+ | public void setIcon(Icon icon) { | ||
+ | this.icon = icon; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | protected void paintComponent(Graphics g) { | ||
+ | |||
+ | super.paintComponent(g); | ||
+ | int w = icon.getIconWidth(); | ||
+ | int h = icon.getIconHeight(); | ||
+ | int x = (800 - w) / 2; | ||
+ | int y = (600 - h) / 2; | ||
+ | icon.paintIcon(this, | ||
+ | } | ||
+ | |||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Test of CD Cover Viewer ===== | ||
+ | 드디어 새로 배운 가상 프록시를 테스트해 볼 때가 되었습니다. 윈도우를 만들고 프레임을 준비하고 메뉴를 설치하고 | ||
+ | 프록시를 생성해주는 %%ImageProxyTestDrive%%클래스를 미리 준비해 뒀습니다. 테스트용 클래스 코드는 다음과 | ||
+ | 같습니다. | ||
+ | |||
+ | <code java> | ||
+ | public class ImageProxyTestDrive { | ||
+ | ImageComponent imageComponent; | ||
+ | JFrame frame = new JFrame(" | ||
+ | JMenuBar menuBar; | ||
+ | JMenu menu; | ||
+ | Hashtable cds = new Hashtable(); | ||
+ | /** | ||
+ | * @param args | ||
+ | */ | ||
+ | public static void main(String[] args) throws Exception{ | ||
+ | ImageProxyTestDrive testDrive = new ImageProxyTestDrive(); | ||
+ | |||
+ | } | ||
+ | |||
+ | public ImageProxyTestDrive() throws Exception{ | ||
+ | cds.put(" | ||
+ | cds.put(" | ||
+ | cds.put(" | ||
+ | cds.put(" | ||
+ | cds.put(" | ||
+ | cds.put(" | ||
+ | cds.put(" | ||
+ | cds.put(" | ||
+ | |||
+ | URL initialURL = new URL((String)cds.get(" | ||
+ | menuBar = new JMenuBar(); | ||
+ | menu = new JMenu(" | ||
+ | menuBar.add(menu); | ||
+ | frame.setJMenuBar(menuBar); | ||
+ | |||
+ | for (Enumeration e = cds.keys(); e.hasMoreElements(); | ||
+ | String name = (String)e.nextElement(); | ||
+ | JMenuItem menuItem = new JMenuItem(name); | ||
+ | menu.add(menuItem); | ||
+ | menuItem.addActionListener(new ActionListener(){ | ||
+ | public void actionPerformed(ActionEvent event){ | ||
+ | imageComponent.setIcon(new ImageProxy(getCDUrl(event.getActionCommand()))); | ||
+ | frame.repaint(); | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | } | ||
+ | |||
+ | // | ||
+ | Icon icon = new ImageProxy(initialURL); | ||
+ | imageComponent = new ImageComponent(icon); | ||
+ | frame.getContentPane().add(imageComponent); | ||
+ | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | ||
+ | frame.setSize(800, | ||
+ | frame.setVisible(true); | ||
+ | } | ||
+ | |||
+ | URL getCDUrl(String name){ | ||
+ | try { | ||
+ | return new URL((String)cds.get(name)); | ||
+ | } catch (MalformedURLException e) { | ||
+ | e.printStackTrace(); | ||
+ | return null; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | **테스트 해 볼 것...** | ||
+ | - **메뉴를 이용하여 다른 CD 커버를 불러옵니다. 이미지 로딩이 완료될 때까지 프록시에서 로딩중이라는 메세지를 보여주는 것을 확인해 봐야 되겠죠? | ||
+ | - **로딩중이라는 메시지가 화면에 표시된 상태에서 윈도우 크기를 조절해 봅시다. 프록시에서 이미지를 로딩하고 있을 때도 스윙 윈도우가 멈추거나 하진 않는지 확인해 봅시다.** | ||
+ | - **%%ImageProxyTestDrive%%에 여러분이 가지고 있는 CD를 추가해 봅시다.** | ||
+ | 다음은 로딩중일 때의 화면입니다. | ||
+ | |||
+ | {{: | ||
+ | |||
+ | 로딩이 완료되면 이런 윈도우가 만들어집니다. | ||
+ | |||
+ | {{: | ||
+ | |||
+ | ===== reference ===== | ||
+ | *[[http:// | ||
+ | ]] | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ |