《Java網路編程》第三章線程講使用回調的方法從線程中返回資訊給對象。例如,如果有多個對象對線程的計算結果感興趣,那麼線程可以儲存一個要回調的對象列表。特定的對象可以通過調用線程類的一個方法把自己添加到這個列表中來完成註冊,表示自己對計算結果高度興趣。如果有多個類的執行個體對結果感興趣,可以定義一個新的Observer interface (介面),所有這些類都要實現這個新介面。這個Observer interface(介面)將聲明回調方法。以上是觀察者模式的典型應用情境。
接下來將上面的需求實現。
1. Observer介面
聲明回調方法update
interface Observer { void update(byte[] digest);}2. 感興趣的觀察者InstanceCallbackDigestUserInterface01
其中將通知者作為其成員變數
public class InstanceCallbackDigestUserInterface01 implements Observer { static Logger logger = LoggerFactory.getLogger(InstanceCallbackDigestUserInterface01.class); private String filename; private byte[] digest; public InstanceCallbackDigestUserInterface01(String filename) { this.filename = filename; } @Override public String toString() { String result = filename + ": "; if (digest != null) { result += DatatypeConverter.printHexBinary(digest); } else { result += "digest not available"; } return result; } @Override public void update(byte[] digest) { this.digest = digest; //只是顯示摘要,但功能更強的類還可以完成其他動作 //如將摘要儲存在一個欄位中,用它啟動另一個錢程,或者對它完成進一步的計算。 logger.info(this.toString()); }}3. 感興趣的觀察者InstanceCallbackDigestUserInterface02以及新線程任務SaveDatabase
public class InstanceCallbackDigestUserInterface02 implements Observer { Logger logger = LoggerFactory.getLogger(InstanceCallbackDigestUserInterface02.class); byte[] digest = null; @Override public void update(byte[] digest) { logger.info("interface02 get this digest"); this.digest = digest; ExecutorService exec = Executors.newCachedThreadPool(); exec.submit(new SaveDatabase(digest)); }}class SaveDatabase implements Runnable { Logger logger = LoggerFactory.getLogger(SaveDatabase.class); private byte[] digest; public SaveDatabase(byte[] digest){ this.digest = digest; } @Override public void run() { String s = DatatypeConverter.printHexBinary(digest); logger.info(s); }}4. 通知者InstanceCallbackDigest
儲存一個要回調的對象列表callback。特定的對象可以通過調用attach方法把自己添加到這個列表中來完成註冊
public class InstanceCallbackDigest implements Runnable { Logger logger = LoggerFactory.getLogger(InstanceCallbackDigest.class); private String filename; private LinkedHashSet<Observer> callback = new LinkedHashSet<>(); public InstanceCallbackDigest(String filename) { this.filename = filename; } public void attach(Observer observer) { callback.add(observer); } public void detach(Observer observer) { callback.remove(observer); } @Override public void run() { try { FileInputStream in = new FileInputStream(filename); MessageDigest sha = MessageDigest.getInstance("SHA-256"); DigestInputStream din = new DigestInputStream(in, sha); while (din.read() != -1) ; // read entire file din.close(); byte[] digest = sha.digest(); for (Observer o:callback ) { logger.info("callback update"); o.update(digest); } } catch (IOException | NoSuchAlgorithmException ex) { System.err.println(ex); } }}5. Test類
public class Test { static Logger logger = LoggerFactory.getLogger(Test.class); public void calculateDigest(String filename, LinkedHashSet<Observer> callback) { InstanceCallbackDigest cb = new InstanceCallbackDigest(filename); //註冊 for (Observer o : callback ) { cb.attach(o); } Thread t = new Thread(cb); t.start(); } @org.junit.Test public void test () { logger.info("main線程"); String[] strs = new String[]{"bw01.txt","pom.xml"};// String[] strs = new String[]{"bw01.txt"}; for (String filename : strs) { //註冊 InstanceCallbackDigestUserInterface01 d = new InstanceCallbackDigestUserInterface01(filename); InstanceCallbackDigestUserInterface02 interface02 = new InstanceCallbackDigestUserInterface02(); LinkedHashSet<Observer> callback = new LinkedHashSet<>(); callback.add(d); callback.add(interface02); //啟動了線程 calculateDigest(filename, callback); //如何通知主線程子線程結束? try { TimeUnit.SECONDS.sleep(3l); } catch (InterruptedException e) { e.printStackTrace(); } } }}6. 類圖
7. 運行結果