| |
脏数据(out-of-date data),指过时的数据。 如果在您的java程序中存在脏数据,将或多或少地给软件系统带来一些问题,如:无法实时地应用已经发生改变的配置,软件系统出现一些莫名其妙的、难以重现的、后果严重的错误等等。尽量避免脏数据的存在是非常有价值的。本文希望能在这方面给同行们一点帮助。 fragment 1. 缓存技术的脏数据问题 /** * a report printer is used to print a report. * * @version 1.0 9/9/2003 * @author bill */ public class reportprinter { /** * constructs a <code>reportprinter</code> instance. */ public reportprinter() { // do something... } /** * prints a printable. * * @param printable the specified printable object */ public void print(printable printable) { graphics g = getgraphics(); g.setfont(getreportfont(printable.getfont()); printable.print(g); } /** * returns the corresponding report font of a java font. * * @param javafont the specified java font * @return the corresponding report font */ private font getreportfont(font javafont) { font reportfont = fontmap.get(javafont); if(reportfont == null) { reportfont = loadfont(javafont); fontmap.put(javafont, reportfont); } return reportfont; } /** * loads the corresponding report font of a java font. * * @param javafont the specified java font * @param the corresponding report font */ protected static font loadfont(font javafont) { font reportfont = null; // do something... return reportfont; } /** * the font map(java font->report font). */ private static hashmap fontmap = new hashmap(); } fragment 1中,由于装载一个java font所对应的report font开销较大,使用了缓存技术来避免这种开销。这是一种常见的提高性能的方式,而且在一般情况下运行良好。但是fragment 1的设计与实现可能是不完备的,因为极有可能一个java font所对应的report font在系统启动之后发生变化,在这种变化发生之后,只有重启软件系统才能装载之,这常常是最终用户的抱怨之一。更可怕的是,类似的这种脏数据的存在还可能带来其它严重的、无法想象的后果。 如何避免使用缓存技术所带来的脏数据问题呢? 在设计、实现和测试时,应该清晰定义缓存数据的更新: i. 不考虑缓存数据的更新,重启软件系统是一种必要的方式; ii. 不考虑缓存数据的更新,缓存数据不可能成为脏数据(但在软件系统中,往往“不可能”会在一次又一次的重构之后变为“可能”); iii. 考虑缓存数据的更新,当源数据变化时,实时更新缓存数据。 fragment 2. singleton模式的脏数据问题 /** * a storage usage handler is used to query the storage usage of users. * * @version 1.0 9/9/2003 * @author bill */ public class storageusagehandler { /** * returns a <code>storageusagehandler</code> instance. * * @return the single <code>storageusagehandler</code> instance */ public static storageusagehandler getstorageusagehandler() { if(handler == null) { handler = new storageusagehandler(); } return handler; } /** * constructs a <code>storageusagehandler</code> instance. */ private storageusagehandler() { users = context.getallusers(); } /** * returns the storage sizes of all the users. * * @return the storage sizes */ public long[] getsizes() { long sizes[] = new long[users.size()]; for(int i = 0; i < users.size(); i++) { sizes[i] = getonesize(users.get(i)); } } /** * returns the storage size of a user. * * @param user the specified user * @return the storage size */ protected long getsize(user user) { // do something... return 0; } /** * the <code>storageusagehandler</code> singleton. */ private static storageusagehandler handler; /** * the users. */ private list users; } 您看出了问题所在吗? fragment 2中,由于没有必要次次实例化storageusagehandler而带来不必要的开销,采用了singleton模式以保证storageusagehandler只被实例化一次。 在实例化sotrageusagehandler时,storageusagehandler的类成员users将被赋值。由于不存在任何对users重新赋值的方法,一直驻留在软件系统中的users将不会发生任何变化。在软件系统启动之后,增加、删除或修改用户的操作经常会发生,而一旦发生这类操作,users就成为了脏数据,fragment 2将无法正常工作。 如何避免使用singleton模式所带来的脏数据问题呢? 对于singleton类的类成员: i. 对于与singleton类外部无依赖关系的类成员,不存在这种问题; ii. 对于依赖于singleton类外部的类成员,且该类成员不存在更新机制,最好是将其去掉,需要时从singleton类外部直接获取;如果这种办法不可行,应提供机制以确保在使用该类成员之前,该类成员已经被更新过。 fragment 3. 类使用的脏数据问题 /** * a storage usage handler is used to query the storage usage of users. * * @version 1.0 9/9/2003 * @author bill */ public class storageusagehandler implements adminhandler { /** * constructs a <code>storageusagehandler</code> instance. */ private storageusagehandler() { users = context.getallusers(); } /** * returns the storage sizes of all users. * * @return the storage sizes */ public long[] getsizes() { long sizes[] = new long[users.size()]; for(int i = 0; i < users.size(); i++) { sizes[i] = getonesize(users.get(i)); } } /** * returns the storage size of a user. * * @param user the specified user * @return the storage size */ protected long getsize(user user) { // do something... return 0; } /** * displays the storage usage of users. * * @param req the http servlet request * @param res the http servlet response * * @throws ioexception * @throws servletexception */ public void process(httpservletrequest req, httpservletresponse res) throws ioexception, servletexception { res.setcontenttype("text/html"); res.setheader("cache-control", "no-cache"); res.setheader("pragma","no-cache"); res.setdateheader("expires", 0); printwriter writer = new printwriter(res.getoutputstream()); long sizes[] = getsizes(); writer.println("<html><title>storage usage</title><body>"); writer.println("<table width='100%'>"); for(int i = 0; i < sizes.length; i++) { writer.print("<tr><td align='center' nowrap>"); writer.print(users.get(i) + ": " + sizes[i]); writer.println("</td></tr>"); } writer.println("</body></html>"); writer.flush(); writer.close(); } /** * the users. */ private list users; } /** * an admin servlet as a http servlet to process the admin http servlet * request and response. * * @version 1.0 9/9/2003 * @author bill */ public class adminservlet extends httpservlet { /** * initiates the configuration. * * @param config the servlet config * * @throws servletexception */ private void initconfig(servletconfig config) throws servletexception { // do somet
|
|