问题来自HeadFirst设计模式第11章代理模式468页的设计谜题:
ImageProxy类似乎有两个有条件语句控制的状态,你能否用另一个设计模式清理这样的代码?你要如何重新设计ImageProxy?

思路:
我们可以使用状态模式实现两个状态,分别是ImageLoaded和ImageUnloaded。然后把if语句内的代码放进去各自的状态中。一开始的状态是ImageLoaded,当ImageIcon取回后就转换道ImageLoaded状态。

下面是自己写的一段参考代码

State类
扩展Icon接口,只是单纯的改个接口名字方便理解
Icon是JavaAPI的内置接口,负责处理图片。

package com.julian.delegatePattern.virtualProxy;

import javax.swing.*;

public interface State extends Icon {}

ImageUnloaded类
如果图片未加载,调用该类。
先打印一行字“Loading CD Cover, please wait...”提醒用户等待。
接着开启新线程加载图片,并修改ImageProxyState的状态为ImageLoaded。

package com.julian.delegatePattern.virtualProxy;

import javax.swing.*;
import java.awt.*;
import java.net.URL;

public class ImageUnloaded implements State {

    ImageProxyState imageProxyState;

    public ImageUnloaded(ImageProxyState imageProxyState) {
        this.imageProxyState = imageProxyState;
    }


    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        g.drawString("Loading CD Cover, please wait...",x+ 300, y + 190);
        if (!imageProxyState.isRetrieving()){
            imageProxyState.setRetrieving(true);
            //open a sub-Thread to load photo
            Thread retrievalThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        ImageIcon imageIcon = new ImageIcon(imageProxyState.getImageURL(),"CD Cover");
                        imageProxyState.setImageIcon(imageIcon);
                        c.repaint();//repaint(0, 0, 0, width, height);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            });
            imageProxyState.setRetrievalThread(retrievalThread);
            imageProxyState.startThread();
        }
        imageProxyState.setCurrentState(imageProxyState.imageLoaded);
    }

    @Override
    public int getIconWidth() {
        return 800;
    }

    @Override
    public int getIconHeight() {
        return 600;
    }
}

ImageLoaded类
如果图片已经加载了,那么调用该类加载图片

package com.julian.delegatePattern.virtualProxy;

import javax.swing.*;
import java.awt.*;

public class ImageLoaded implements State {
    ImageProxyState imageProxyState;

    public ImageLoaded(ImageProxyState imageProxyState) {
        this.imageProxyState = imageProxyState;
    }

    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        imageProxyState.paintIconByImageIcon(c,g,x,y);
    }

    @Override
    public int getIconWidth() {
        return imageProxyState.getIconWidthByImageIcon();
    }

    @Override
    public int getIconHeight() {
        return imageProxyState.getIconHeightByImageIcon();
    }

    public void setImageIcon(ImageIcon imageIcon) {
        this.imageProxyState.setImageIcon(imageIcon);
    }
}



ImageProxyState
Icon的代理类。有两个State状态 imageLoaded和 imageUnloaded。
当调用它的方法时,会委托给它当前状态处理。

package com.julian.delegatePattern.virtualProxy;

import javax.swing.*;
import java.awt.*;
import java.net.URL;

public class ImageProxyState implements Icon {
    State imageLoaded;
    State imageUnloaded;
    State currentState;

    ImageIcon imageIcon;
    URL imageURL;
    Thread retrievalThread;
    boolean retrieving = false;

    public ImageProxyState(URL imageURL) {
        this.imageURL = imageURL;
        imageLoaded = new ImageLoaded(this);
        imageUnloaded = new ImageUnloaded(this);
        currentState = imageUnloaded;
    }

    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        currentState.paintIcon(c,g,x,y);
    }

    @Override
    public int getIconWidth() {
        return currentState.getIconWidth();
    }

    @Override
    public int getIconHeight() {
        return currentState.getIconHeight();
    }

    public boolean isRetrieving() {
        return retrieving;
    }

    public void setCurrentState(State currentState) {
        this.currentState = currentState;
    }

    public void setImageIcon(ImageIcon imageIcon) {
        this.imageIcon = imageIcon;
    }

    public URL getImageURL() {
        return imageURL;
    }

    public void setRetrievalThread(Thread retrievalThread) {
        this.retrievalThread = retrievalThread;
    }

    public void setRetrieving(boolean retrieving) {
        this.retrieving = retrieving;
    }

    public void startThread(){
        retrievalThread.start();
    }

    public int getIconWidthByImageIcon(){
        return imageIcon.getIconWidth();
    }

    public int getIconHeightByImageIcon(){
        return imageIcon.getIconHeight();
    }

    public void paintIconByImageIcon(Component c, Graphics g, int x, int y){
        imageIcon.paintIcon(c,g,x,y);
    }
}

ImageProxyStateTestDrive
建立框架和菜单并测试代码

package com.julian.delegatePattern.virtualProxy;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;

public class ImageProxyStateTestDrive {
    ImageComponent imageComponent;
    JFrame jFrame = new JFrame("CD Cover Viewer");//set the title for the frame
    JMenuBar jMenuBar;
    JMenu jMenu;
    Hashtable cds = new Hashtable();

    public static void main(String[] args) throws Exception {
        ImageProxyStateTestDrive imageProxyTestDrive = new ImageProxyStateTestDrive();
    }

    public ImageProxyStateTestDrive() throws Exception {
        cds.put("Ballet Pose","https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=1736727202,1355489933&fm=26&gp=0.jpg");
        cds.put("Naruto","https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1062924988,1223376387&fm=26&gp=0.jpg");
        cds.put("Selected Ambient Works, Vol. 2","https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1265870196,2883097596&fm=26&gp=0.jpg");
        cds.put("kimonoBaiDu","https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1598667455000&di=c90ebc533f1a4e1109a18d52ee353ee7&imgtype=0&src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2F454d60b63dbc3858331bbc4bc8757aef329e5b59231b6-bcV3m4_fw658");

        URL initialURL = new URL((String)cds.get("Selected Ambient Works, Vol. 2"));
        jMenuBar = new JMenuBar();
        jMenu = new JMenu("fAVORITE cds");//set the title of the menu
        jMenuBar.add(jMenu);
        jFrame.setJMenuBar(jMenuBar);
        for (Enumeration e = cds.keys();e.hasMoreElements();){
            String name = (String)e.nextElement();
            JMenuItem jMenuItem = new JMenuItem(name);
            jMenu.add(jMenuItem);
            jMenuItem.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    imageComponent.setIcon(new ImageProxyState(getCDUrl(e.getActionCommand())));
                    jFrame.repaint();
                }
            });
        }

        //建立框架和菜单build frame and menu
        Icon icon = new ImageProxyState(initialURL);
        imageComponent = new ImageComponent(icon);
        jFrame.getContentPane().add(imageComponent);
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jFrame.setSize(1000,800);
        jFrame.setVisible(true);
        jFrame.setLocation(450,100);
    }

    URL getCDUrl(String name){
        try {
            return new URL((String)cds.get(name));
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return null;
        }
    }
}