Java設計モード(四):プロトタイプモード


プロトタイプモード
プロトタイプモードでは、プロトタイプインスタンスで作成オブジェクトの種類を指定し、これらのプロトタイプをコピーして新しいオブジェクトを作成します.プロトタイプ・モードは、1つのオブジェクトから別のカスタマイズ可能なオブジェクトを作成する作成型設計モードであり、作成の詳細を知る必要はありません.

履歴書クラスには、名前が必要で、性別や年齢を設定したり、職歴を設定したりすることができます.最後に同じ履歴書を3部コピーします.
/**
 * Resume
 */
public class Resume {

    private String name;

    private String sex;

    private String age;

    private String timeArea;

    private String company;

    public Resume(String name) {
        this.name = name;
    }

    public void setPersonInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    public void setWorkExperience(String timeArea, String company) {
        this.timeArea = timeArea;
        this.company = company;
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age='" + age + '\'' +
                ", timeArea='" + timeArea + '\'' +
                ", company='" + company + '\'' +
                '}';
    }
}

/**
 * Main
 */
public class Main {

    public static void main(String[] args) {
        Resume a = new Resume("God.Gao");
        a.setPersonInfo("woman", "20");
        a.setWorkExperience("2014-2018", "XXXCompany");

        Resume b = new Resume("God.Gao");
        b.setPersonInfo("woman", "20");
        b.setWorkExperience("2014-2018", "XXXCompany");

        Resume c = new Resume("God.Gao");
        c.setPersonInfo("woman", "20");
        c.setWorkExperience("2014-2018", "XXXCompany");

        System.out.println(a.toString());
        System.out.println(b.toString());
        System.out.println(c.toString());
    }
}

この書き方は分かりやすく、操作しやすいです.しかし、同じ履歴書を20部必要とすると、20回のインスタンス化が必要であり、newごとに1回のコンストラクション関数を実行する必要があります.コンストラクション関数の実行時間が長い場合、この初期化操作を複数回実行するのは本当に効果的ではありません.いずれかのプロパティの値を変更する必要がある場合は、20回変更する必要があります.これは面倒で、効率も低いです.
プロトタイプモードの使用による改善
JavaにおけるObjectクラスはすべてのクラスのベースクラスであり、ObjectクラスはJavaオブジェクトをコピーできるclone()メソッドを提供しているが、clone()を実装する必要があるJavaクラスはCloneableインタフェースを実装する必要があり、プロトタイプモードを完了することができる.
/**
 * Resume
 */
public class Resume implements Cloneable {

    private String name;

    private String sex;

    private String age;

    private String timeArea;

    private String company;

    public Resume(String name) {
        this.name = name;
    }

    public void setPersonInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    public void setWorkExperience(String timeArea, String company) {
        this.timeArea = timeArea;
        this.company = company;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age='" + age + '\'' +
                ", timeArea='" + timeArea + '\'' +
                ", company='" + company + '\'' +
                '}';
    }
}

/**
 * Main
 */
public class Main {

    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a = new Resume("God.Gao");
        a.setPersonInfo("woman", "20");
        a.setWorkExperience("2014-2018", "XXXCompany");

        Resume b = (Resume) a.clone();

        Resume c = (Resume) a.clone();

        System.out.println(a.toString());
        System.out.println(b.toString());
        System.out.println(c.toString());
    }
}


プロトタイプモードは、実際にはオブジェクトのクローンです.一般的に初期化された情報が変化しない場合、クローンを作成するのが最善の方法です.これにより、オブジェクトの作成の詳細が非表示になり、パフォーマンスが大幅に向上します.
上記のコードで実装されたクローンは、浅いクローンに属します.浅いクローンがある以上、深いクローンがあります.
浅いクローンVS深いクローン
浅いクローン
浅いクローンとは、コピーされたオブジェクトのすべての変数が元のオブジェクトと同じ値を含み、他のオブジェクトへの参照はすべて元のオブジェクトを指します.--『大話デザインモード』
データ型が基本データ型のメンバー変数の場合、浅いクローンは直接値を渡します.つまり、この属性値を新しいオブジェクトにコピーします.データ型が参照データ型のメンバー変数、たとえばメンバー変数が配列、クラスのオブジェクトなどである場合、浅いクローンは参照伝達されます.つまり、そのメンバー変数の参照値(メモリアドレス)を新しいオブジェクトにコピーするだけです.したがって、元のオブジェクトとそのコピーは同じオブジェクトを参照します.この場合、あるオブジェクトでメンバー変数の値を変更すると、別のオブジェクトのメンバー変数の値に影響します.
深いクローン
名前の通り、オブジェクトのすべての基本データ型のメンバー変数値をコピーするだけでなく、すべての参照データ型のメンバー変数のストレージスペースを申請し、オブジェクトが到達するまで、各参照データ型のメンバー変数が参照するオブジェクトをコピーします.すなわち、オブジェクトを深くクローンするには、オブジェクト全体(オブジェクトの参照タイプを含む)をコピーします.これにより、あるオブジェクトのメンバー変数の値が変更され、別のオブジェクトのメンバー変数の値に影響を与えることはありません.
浅いクローン&深いクローンの例の比較
例:次の履歴書(Resume)クラスをコピーします.
public class Resume {

    private String name;

    private String sex;

    private String age;

    private WorkExperience workExperience;

    public Resume(String name) {
        this.name = name;
        this.workExperience = new WorkExperience();
    }

    public void setPersonInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    public void setWorkExperience(String workDate, String company) {
       this.workExperience.setWorkDate(workDate);
       this.workExperience.setCompany(company);
    }

    @Override
    public String toString() {
        return "Resume{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age='" + age + '\'' +
                ", workExperience=" + workExperience +
                '}';
    }
}

/**
 * WorkExperience
 */
public class WorkExperience {

    private String workDate;

    private String company;

    public String getWorkDate() {
        return workDate;
    }

    public void setWorkDate(String workDate) {
        this.workDate = workDate;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    @Override
    public String toString() {
        return "WorkExperience{" +
                "workDate='" + workDate + '\'' +
                ", company='" + company + '\'' +
                '}';
    }
}
  • 浅いクローンを使用して
  • を複製
    /**
     * Resume
     */
    public class Resume implements Cloneable {
    
        private String name;
    
        private String sex;
    
        private String age;
    
        private WorkExperience workExperience;
    
        public Resume(String name) {
            this.name = name;
            this.workExperience = new WorkExperience();
        }
    
        public void setPersonInfo(String sex, String age) {
            this.sex = sex;
            this.age = age;
        }
    
        public void setWorkExperience(String workDate, String company) {
           this.workExperience.setWorkDate(workDate);
           this.workExperience.setCompany(company);
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        @Override
        public String toString() {
            return "Resume{" +
                    "name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", age='" + age + '\'' +
                    ", workExperience=" + workExperience +
                    '}';
        }
    }
    
    /**
     * WorkExperience
     */
    public class WorkExperience implements Cloneable {
    
        private String workDate;
    
        private String company;
    
        public String getWorkDate() {
            return workDate;
        }
    
        public void setWorkDate(String workDate) {
            this.workDate = workDate;
        }
    
        public String getCompany() {
            return company;
        }
    
        public void setCompany(String company) {
            this.company = company;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        @Override
        public String toString() {
            return "WorkExperience{" +
                    "workDate='" + workDate + '\'' +
                    ", company='" + company + '\'' +
                    '}';
        }
    }
    
    /**
     * Main
     */
    public class Main {
    
        public static void main(String[] args) throws CloneNotSupportedException {
            Resume a = new Resume("God.Gao");
            a.setPersonInfo("woman", "20");
            a.setWorkExperience("2014-2018", "XXXCompany");
    
            Resume b = (Resume) a.clone();
            b.setWorkExperience("2015-2019", "xiaokejiCompany");
    
            Resume c = (Resume) a.clone();
            c.setWorkExperience("2014-2017", "yunCompany");
    
            System.out.println(a.toString());
            System.out.println(b.toString());
            System.out.println(c.toString());
        }
    }
    

    結果:
    Resume{name='God.Gao', sex='woman', age='20', workExperience=WorkExperience{workDate='2014-2017', company='yunCompany'}}
    Resume{name='God.Gao', sex='woman', age='20', workExperience=WorkExperience{workDate='2014-2017', company='yunCompany'}}
    Resume{name='God.Gao', sex='woman', age='20', workExperience=WorkExperience{workDate='2014-2017', company='yunCompany'}}
    

    その結果、浅いクローンを使用してオブジェクトをコピーすると、オブジェクトに参照タイプのメンバー変数が存在する場合、そのメンバー変数の参照アドレスがコピーされるだけで、コピーされた新しいオブジェクトを指すメンバー変数はありません.これにより、あるオブジェクトがメンバー変数の値を変更し、他のオブジェクトのメンバー変数の値に影響を及ぼします.
  • 深いクローンを使用して
  • を複製
    深いクローンには2つの実装方法がある:1.clone()の書き換え方法、2.シーケンス化を使用します.
  • clone()書き換え方法
  • /**
     * Resume
     */
    public class Resume implements Cloneable {
    
        private String name;
    
        private String sex;
    
        private String age;
    
        private WorkExperience workExperience;
    
        public Resume(String name) {
            this.name = name;
            this.workExperience = new WorkExperience();
        }
    
        public void setPersonInfo(String sex, String age) {
            this.sex = sex;
            this.age = age;
        }
    
        public void setWorkExperience(String workDate, String company) {
           this.workExperience.setWorkDate(workDate);
           this.workExperience.setCompany(company);
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Resume resume = (Resume) super.clone();
            //             
            resume.workExperience = (WorkExperience) this.workExperience.clone();
            return resume;
        }
    
        @Override
        public String toString() {
            return "Resume{" +
                    "name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", age='" + age + '\'' +
                    ", workExperience=" + workExperience +
                    '}';
        }
    }
    
    /**
     * WorkExperience
     */
    public class WorkExperience implements Cloneable {
    
        private String workDate;
    
        private String company;
    
        public String getWorkDate() {
            return workDate;
        }
    
        public void setWorkDate(String workDate) {
            this.workDate = workDate;
        }
    
        public String getCompany() {
            return company;
        }
    
        public void setCompany(String company) {
            this.company = company;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        @Override
        public String toString() {
            return "WorkExperience{" +
                    "workDate='" + workDate + '\'' +
                    ", company='" + company + '\'' +
                    '}';
        }
    }
    
    /**
     * Main
     */
    public class Main {
    
        public static void main(String[] args) throws CloneNotSupportedException {
            Resume a = new Resume("God.Gao");
            a.setPersonInfo("woman", "20");
            a.setWorkExperience("2014-2018", "XXXCompany");
    
            Resume b = (Resume) a.clone();
            b.setWorkExperience("2015-2019", "xiaokejiCompany");
    
            Resume c = (Resume) a.clone();
            c.setWorkExperience("2014-2017", "yunCompany");
    
            System.out.println(a.toString());
            System.out.println(b.toString());
            System.out.println(c.toString());
        }
    }
    
  • シーケンス化方式
  • を用いる.
    注意:深いクローンをシーケンス化で実装する場合、関連するクラスはSerializableインタフェースを実装する必要があります.
    /**
     * Resume
     */
    public class Resume implements Serializable {
    
        private static final long serialVersionUID = 6395782880219068537L;
    
        private String name;
    
        private String sex;
    
        private String age;
    
        private WorkExperience workExperience;
    
        public Resume(String name) {
            this.name = name;
            this.workExperience = new WorkExperience();
        }
    
        public void setPersonInfo(String sex, String age) {
            this.sex = sex;
            this.age = age;
        }
    
        public void setWorkExperience(String workDate, String company) {
            this.workExperience.setWorkDate(workDate);
            this.workExperience.setCompany(company);
        }
    
        public Object deepClone() {
            ByteArrayOutputStream byteArrayOutputStream = null;
            ObjectOutputStream objectOutputStream = null;
            ByteArrayInputStream byteArrayInputStream = null;
            ObjectInputStream objectInputStream = null;
    
            try {
                //        
                byteArrayOutputStream = new ByteArrayOutputStream();
                objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                //             
                objectOutputStream.writeObject(this);
    
                //        
                byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
                objectInputStream = new ObjectInputStream(byteArrayInputStream);
                //      
                return objectInputStream.readObject();
            } catch (IOException | SecurityException | ClassNotFoundException e) {
                return null;
            } finally {
                 //    
                try {
                    if (objectInputStream != null) {
                        objectInputStream.close();
                    }
                    if (byteArrayInputStream != null) {
                        byteArrayInputStream.close();
                    }
                    if (objectOutputStream != null) {
                        objectOutputStream.close();
                    }
                    if (byteArrayOutputStream != null) {
                        byteArrayOutputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        @Override
        public String toString() {
            return "Resume{" +
                    "name='" + name + '\'' +
                    ", sex='" + sex + '\'' +
                    ", age='" + age + '\'' +
                    ", workExperience=" + workExperience +
                    '}';
        }
    }
    
    /**
     * WorkExperience
     */
    public class WorkExperience implements Serializable {
    
        private static final long serialVersionUID = -939371138425810409L;
    
        private String workDate;
    
        private String company;
    
        public String getWorkDate() {
            return workDate;
        }
    
        public void setWorkDate(String workDate) {
            this.workDate = workDate;
        }
    
        public String getCompany() {
            return company;
        }
    
        public void setCompany(String company) {
            this.company = company;
        }
    
        @Override
        public String toString() {
            return "WorkExperience{" +
                    "workDate='" + workDate + '\'' +
                    ", company='" + company + '\'' +
                    '}';
        }
    }
    
    
    /**
     * Main
     */
    public class Main {
    
        public static void main(String[] args) {
            Resume a = new Resume("God.Gao");
            a.setPersonInfo("woman", "20");
            a.setWorkExperience("2014-2018", "XXXCompany");
    
            Resume b = (Resume) a.deepClone();
            b.setWorkExperience("2015-2019", "xiaokejiCompany");
    
            Resume c = (Resume) a.deepClone();
            c.setWorkExperience("2014-2017", "yunCompany");
    
            System.out.println(a.toString());
            System.out.println(b.toString());
            System.out.println(c.toString());
        }
    }
    

    結果:
    Resume{name='God.Gao', sex='woman', age='20', workExperience=WorkExperience{workDate='2014-2018', company='XXXCompany'}}
    Resume{name='God.Gao', sex='woman', age='20', workExperience=WorkExperience{workDate='2015-2019', company='xiaokejiCompany'}}
    Resume{name='God.Gao', sex='woman', age='20', workExperience=WorkExperience{workDate='2014-2017', company='yunCompany'}}
    

    小結
    新しいオブジェクトの作成が複雑な場合、プロトタイプモードを使用してオブジェクトの作成プロセスを簡略化するとともに、オブジェクトを再初期化することなく、オブジェクトの実行時の状態を動的に取得する効率を向上させることができます.元のオブジェクトが変化(アトリビュートの増加または減少)した場合、コードを変更することなく、他のクローンオブジェクトも変化します.深いクローンを実現するには複雑なコードが必要であり、クラスごとにクローン方法を実現する必要がある場合があります.これは新しいクラスにとって難しくありませんが、既存のクラスを改造する場合は、ソースコードを修正する必要があり、オープンクローズの原則に反しています.