2011年3月31日 星期四

[OO 設計原則] Low of Demeter


前言 :
Law of Demeter (LoD),最少知識原則 : 一個物件盡可能的少知道其他物件,反之,物件本身提供最少訊息給其他物件。那麼"知識"一詞又是代表何種意義??就是,一個物件本身提供最少的 public variable 及 public method 給外界使用.

狹義的LoD :
如果兩個 class 不必彼此直接溝通,那麼這兩個 class 就不應該直接互相發生作用,如果其中一個 class 有必須要與之溝通,則可以透過第三者來 Call Forwarding.

若滿足 :
- 物件本身(this)
- 以參數形式傳送給該物件method 的物件
- 此物件 variable 中直接 reference 的物件,i.e. 此物件內的物件 variable
- 此物件為聚集物件,則聚集中的所有物件抑是
- 此物件所建構的物件

其中之一,則為可以直接溝通的 class ,否則就必須透過第三者來轉發. 由於轉發其他物件的訊息給另一個物件,所以系統中會存在許許多多的小method 作為轉發訊息之用,這也使得設計師在觀察系統的 UML 圖時,會造成困惑,也因為訊息必須透過轉發而不是直接溝通,所以身在兩個遙遠的物件,必須透過多層次的溝通,也就產生了物件之間的不容易協調. 所以 Kirk Knoernschild 在 JAVA Design 一書中提到,為了克服這種狹義的 LoD ,我們可以配合 DIP (Dependency Inversion Principle) 來加入抽象 class,使得兩個不能溝通的物件彼此依賴於此 abstract class.

廣義的LoD :
廣義的 LoD 就是對物件之間的通訊傳輸量的控制,目的為了控制資訊的超載. 在軟體設計中,一個 class 設計的好壞,取決於將自己內部資訊及時做隱藏的程度,透過 information hiding及 encapsulation 來達成. 而運用 LoD 於系統上時必須注意 :
1. 設計系統中的 class 時,應當建構出耦合度越低越好 :
若是兩個 class 耦合度越高,當系統面臨修改或是維護時,此兩個 class 會產生瓶頸,造成修改上的困難,因為可能在這裡動一刀,另一個 class 就完全不能運作了!我們可以透過降低耦合度來提高 class 的 reusability.

2. 在 class 設計上,應當降低 field 的 Accessibility :
透過 modification 來提供存取的限制。一個 class 被製造發佈後,提供者就無法將原 先的 public variable or public method 更改為 private,因為可能已經有部分用戶這在使用,若更改,則產生所有 client 的影響,所以設計 class 應當降低對外開放的資訊,並審慎的提供外部資訊.

3. 設 class 設計上,只要有可能,就應當將此 class 設計為 immutable class :
immutable class 之五大原則 :
A. 不提供任何可修改物件內容的 method
B. 保證沒有 method 可以被 override。i.e. 使 class 為 final or 使所有 method 為 final or 使 constructor 為 private,並且提供一個 public static factory method
C. 令所有 field 為 final 並不使用同步機制
D. 令所有的 field 為 private
E. 確保對所有的 mutable components 提供 exclusive access

4. 對其他 class 的 reference 上,一個物件對其物件的 reference 應該降到最低 :
當所 reference 的物件提供相當多的資訊時,我們就必須降低對其資訊的依賴,達成此項目的.

補充說明 :
Dependency Inversion Principle :
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.

[ Data Structures with Java ] Section 18.4 : The STree Iterator

Preface : 
Iterators have basic operations that allow a program to scan the elements in a collection and access their values. They also allow removal of an element as an update operation. The iterator methods are hasNext(), next() and remove(). Our task is to implement these methods as well as implement the STree class method iterator() that creates an Iterator object and gives it an initial value. 

The STree Iterator : 
In Section 17.2, we introduced an iterative inorder scan of a binary tree. The algorithm used a stack to store node references when we descended down a path to locate the next element. Popping the stack allowed us to reverse steps and move up the path. The design simulated the recursive version of the inorder scan. With the STree iterator, we implement an iterative traversal of the elements using the STNode parent field. 
- The Private Variables and Constructor of the STree Iterator Class 
Like the LinkedList iterator, we constructor the STree iterator using an inner class, IteratorImpl, that implements the Iterator interface. The private portion of IteratorImpl includes the variable nextNode, which references the next node in the inorder traversal of the tree. Its counterpart, lastReturned, references the last node whose value is returned by next(). This variable designates the node that is deleted by the iterator remove() method. The integerexpectedModCount is an inner class variable that corresponds to the value of modCount in the STree class. Comparing the two allows the iterator to determine whether an unexpected change occurred in the tree. For instance, the program may insert a new element using the STree class add(). This invalidates an iterator. The only way we can update the tree during an iterator scan is to call the iterator method remove(). Below is the implementation of the constructor of class IteratorImple : 

- IteratorImpl inner class inside STree class: Constructor and private variables
  1.     private class IteratorImpl implements Iterator{  
  2.         // set expectedModCount to the number of list changes   
  3.         // at the time of iterator creation  
  4.         private int expectedModCount = modCount;  
  5.         // node of the last value returned by next() if that  
  6.         // value was deleted by the iterator method remove().  
  7.         private STNode lastReturned = null;  
  8.         // node whose value is returned a subsequent call to next;  
  9.         private STNode nextNode = null;  
  10.           
  11.         IteratorImpl(){  
  12.             nextNode = root;  
  13.             if(nextNode!=null)  
  14.                 while(nextNode.left!=null)  
  15.                     nextNode = nextNode.left;  
  16.         }  
  17. ...  
  18. }  

The constructor initializes nextNode to point at the minimum element in the tree. Find the element by starting at the root and traversing the chain of left subtrees. The process stops at a node with a null left subtree. This become the first node the iterator visits in the inorder scan. 

The STree Iterator Public Method : 
The STree method iterator() simply creates and returns an IteratorImpl object. The iterator is positioned at the minimum element whose value is the leftmost node in the tree. To follow up, lets check the implementation of methods in class IteratorImpl. 
- Implementing next() 
To implement the method next(), we must execute a series of iterative steps that move use from the current node to the next node in order. We do not look at the left branch of the current tree node, because it contains smaller values that we have already visited. We must either descend down the right subtree or move up the tree. We summarize the possibilities with the following rules : 



  • If the right subtree is not empty, obtain the next node in order by moving to the right child and then moving left until we encounter a null subtree.




  • If the right subtree is empty, obtain the next node in order by following a chain of parent references until we find a parent, P, for which our current node, nodePtr, is a left child. Node P is the next node in order. When this situation occurs, all the nodes on the left subtree of P have been visited, and it is time to visit P




  • The implementation of next() follows these two rules even when the method extracts the value of the last node. In the following implementation, the private method checkIteratorState() verifies that the value of expectedModCount in the inner class IteratorImpl equals the value of modCount in the STree class. If the values are not equal, an unexpected change has occurred in the tree and an exception is thrown : 

    - Implementation of class IteratorImpl : (Except method remove())
    1. private class IteratorImpl implements Iterator{  
    2.     // set expectedModCount to the number of list changes   
    3.     // at the time of iterator creation  
    4.     private int expectedModCount = modCount;  
    5.     // node of the last value returned by next() if that  
    6.     // value was deleted by the iterator method remove().  
    7.     private STNode lastReturned = null;  
    8.     // node whose value is returned a subsequent call to next;  
    9.     private STNode nextNode = null;  
    10.       
    11.     IteratorImpl(){  
    12.         nextNode = root;  
    13.         if(nextNode!=null)  
    14.             while(nextNode.left!=null)  
    15.                 nextNode = nextNode.left;  
    16.     }  
    17.       
    18.     protected void checkIteratorState(){  
    19.         if(expectedModCount!=modCount) throw new ConcurrentModificationException("Concurrent Modification Exception!");  
    20.     }  
    21.       
    22.     @Override  
    23.     public boolean hasNext() {  
    24.         return nextNode!=null;  
    25.     }  
    26.   
    27.     @Override  
    28.     public T next() {  
    29.         checkIteratorState();  
    30.         if(nextNode==null)  
    31.             throw new NoSuchElementException();  
    32.         lastReturned = nextNode;  
    33.         STNode p;  
    34.         if(nextNode.right!=null) {  
    35.             nextNode = nextNode.right;  
    36.             while(nextNode.left!=null) nextNode = nextNode.left;  
    37.         } else {  
    38.             p = nextNode.parent;  
    39.             while(p!=null && p.right == nextNode) {  
    40.                 nextNode = p;  
    41.                 p = nextNode.parent;  
    42.             }  
    43.             nextNode = p;  
    44.         }  
    45.         return lastReturned.nodeValue;  
    46.     }  
    47.   
    48.     @Override  
    49.     public void remove() {  
    50.         throw new java.lang.UnsupportedOperationException();              
    51.     }  
    52.   
    53. }  

    [ Python 常見問題 ] How to shift a datetime object by 12 hours in python

    Source From   Here   Question   Datetime   objects hurt my head for some reason. I am writing to figure out   how to shift a date time obje...