/** * 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);
/** * 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(); }
/** * 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只被實例化一次。
對于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 {
/** * 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