関係行動設計へのオブジェクト



本篇
这些パターン主要是处理数据从对象以及关系型数据库之间的传递问题.

只有データソース層的问题
  • 如果要处理大量的数据,我们需要一口气把这些数据读出来,再一口气写回去;
  • 並行性

  • 労働単位
    ワークパターンの単位は、どのドメインオブジェクトが変更されたかを追跡する方法を記述します(変更されたオブジェクトだけがデータベースで更新される必要がある).
    记录那些对象改变了,然后更新数据库中对应的记录.
    有一个労働単位对象,包含了4个 list一个 commit()方法.
  • new list新增的对象,需要插入到数据库中
  • dirty list修改过的对象
  • clean list未修改过的对象(实际情况下, clean list无需创建)
  • delete list删除的对象,需要在数据库中删除相应记录
  • 有两种方法更新这些リスト:

  • 呼び出し元の登録在代码附近增加把对象放进リスト里的操作,比例新しい出一个对象后立即把它放进 new list中;

  • オブジェクト登録:让对象自己把自己放进相应的リスト中,比如在セッター方法里和构造方法里添加相应的代码.
  • 以オブジェクト登録为例(线程安全相关的代码省略):
    public class Song extends DomainObject {
        private String name;
        private String artist;
        ...
    
        public Song(String name, String artist, ...) {
            this.name = name;
            ...
            UnitOfWork.registerNew(this);
        }
    
        public void setName(String name) {
            this.name = name;
            ...
            UnitOfWork.registerDirty(this);
        }
    }
    
    public class UnitOfWork {
        private static ThreadLocal current = new ThreadLocal();
    
        private List<DomainObject> newObjects = new ArrayList<DomainObject>();
        private List<DomainObject> dirtyObjects = new ArrayList<DomainObject>();
        private List<DomainObject> deletedObjects = new ArrayList<DomainObject>();
    
        // put obj into the New list
        public void registerNew(DomainObject obj) {
            Assert.notNull(obj.getId(), "id is null", );
            Assert.isTrue(!dirtyObjects.contains(obj), "object is dirty");
            Assert.isTrue(!deletedObjects.contains(obj), "object is deleted");
            Assert.isTrue(!newObjects.contains(obj), "object is new");
            newObjects.add(obj);
        }
    
        public void registerDirty(DomainObject obj) {...}
        public void registerDelete(DomainObject obj) {...}
    
        // Let the data-source layer to do the db-things
        public void commit() {
            for (DomainObject obj : newObjects) {
                DataMapper.getMapper(obj.getClass()).insert(obj);
            }
            for (DomainObject obj : dirtyObjects) {
                DataMapper.getMapper(obj.getClass()).update(obj);
            }
            for (DomainObject obj : deletedObjects) {
                DataMapper.getMapper(obj.getClass()).delete(obj);
            }
        }
    }
    

    労働単位的优缺点

    長所
  • 高い粘着力:高内聚,关于已更改、添加或删除内容的所有信息都包含在每个线程的单个对象中
  • 简单
  • 高效

  • 短所
  • 容易忘记将对象放入相应的列表中

  • アイデンティティマップ
    各オブジェクトがマップ内のすべてのロードされたオブジェクトを維持することによって一度だけロードされることを保証します.それらを参照するときにマップを使用してオブジェクトを検索します.
    只要用来防止同一个数据被多次从数据库中读出来.
    实现的方法是对数据库的每一个表构造一个マップ即代码中的 Map<Class, IdentityMap> singleton利用数据库中的キー来判断那些记录已经被读出来了.
    public class IdentityMap<E> {
        private Map<Long, E> map = new HashMap<Long, E>();
        private static Map<Class, IdentityMap> singleton = 
            new HashMap<Class, IdentityMap>();
    
        public static <E> IdentityMap<E> getInstance(E e) {
            IdentityMap<E> result = singletons.get(e.getClass());
            if (result == null) {
                result = new IdentityMap<E>();
                singletons.put(e.getClass(), result);
            }
            return result;
        }
    
        public void put(long id, E o) {
            map.put(id, o);
        }
    
        public E get(long id) {
            return map.get(id);
        }
    }
    

    アイデンティティマップ的优缺点

    長所
  • 可以直接使用 ==比较对象,而不是使用 equals()
  • 简单
  • 高效

  • 短所
  • 暂时没有

  • 怠惰荷重
    必要なデータの全てを含まないオブジェクトですが、取得する方法を知っています.
    怠惰荷重旨在降低对数据库的读操作,只读需要的数据.

    实现怠惰荷重的四种方法:

  • 怠惰な初期化:每一个对象在创建的时候,它的所有属性都设置为NULL需要某个属性的时候再从数据库里读取这个属性;
  • バーチャルプロキシ
  • バリューホルダー

  • ゴースト与怠惰な初期化类似,只是需要某个属性时,从数据库里读取这个对象的全部属性.

  • 怠惰な初期化
    public class Student {
        private int id;
        private String firstName;
        private String lastName;
        ...
    
        public String getLastName() {
            if(this.lastName == null) {
                // load lastName from db
                Record record = gateway.find(this.id);
                this.lastName = record.get("lastName");
            }
            return this.lastName;
        }
    
        ...
    }
    

    ゴースト
    public class Student {
        private int id;
        private String firstName;
        private String lastName;
        ...
    
        public String getLastName() {
            if(this.lastName == null) {
                load();
            }
            return this.lastName;
        }
    
        ...
    
        // load all the attributes from db
        public void load() {
            Record record = gateway.find(this.id);
            if(this.lastName == null) {
                this.lastName = record.get("lastName");
            }
            // load other attributes
            ...
    
        }
    }
    

    仮想プロキシ
    与代理模式相同.
    プロキシは元のオブジェクトへのアクセスを制御し、元のオブジェクトへのリクエストの前または後に実行することができます.
    首先创建一个接口,里面的方法和实体类里的一样,
    public interface StudentI {
        public int getId();
        public void setId(int id);
        public String getFitstName();
        public void setFitstName(String firstName);
        ...
    }
    
    public class StudentProxy implements StudentI {
        // the real object
        private StudentI source;
    
        private StudentI getSource() {
            if (source == null) {
                load();
            }
        }
    
        public void setLastName(String lastName) {
            getSource().setLastName(lastName);
        }
    
        public String getLastName() {
            return getSource().getLastName();
        }
    
        ...
    }
    

    バリューホルダー
    与仮想プロキシ类似,只是バリューホルダー类不像仮想プロキシ一样拥有一样的方法接口.所有的实体类都共享一个バリューホルダー.
    // Java function to illustrate
    // Lazy Initialization in
    // Lazy Loading Design Pattern
    public class ValueHolder<T> {
        private T value;
        private readonlyFunc<object, T> valueRetrieval;
        // Constructor
        public ValueHolder(Func<object, T> valueRetrieval) {
            valueRetrieval= this.valueRetrieval;
        }
    
        // We'll use the signature "GetValue" for convention
        public T GetValue(object parameter) {
            if (value == null)
                value = valueRetrieval(parameter);
            return value;
        }
    }
    

    怠惰荷重的优缺点

    長所
  • 高效

  • 短所
  • 复杂
  • 继承问题:如果实体类间有继承关系,那么会使得不知道该创建哪个类
  • 有时候一次性读取大量的记录效率更高