问题来自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; } } }