English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Androidのプログラミング設計パターン「プロトタイプパターン」の例を詳細に説明

この記事では、Androidプログラミング設計パターンのプロトタイプパターンについて説明します。皆さんに参考にしていただくために共有します。以下の通りです:

一、紹介

プロトタイプモードは、生成型のパターンです。プロトタイプという言葉は、このモデルがあるサンプルインスタンスを持っていることを示しています。ユーザーはこのサンプルオブジェクトから内部属性が一致するオブジェクトをコピーし、このプロセスは私たちが一般的に「クローン」と言うものです。コピーされたインスタンスは、私たちが「プロトタイプ」と呼ぶものです。このプロトタイプもカスタマイズ可能です。プロトタイプモデルは、複雑または構築が時間がかかるインスタンスを作成する際に多く使用されます。なぜなら、この場合、既存のインスタンスをコピーすることで、プログラムの実行がより効率的になります。

二、定義

プロトタイプインスタンスを使用してオブジェクトの種類を指定し、これらのプロトタイプをコピーして新しいオブジェクトを作成します。

三、使用シーン

(1)クラスの初期化には非常に多くのリソースが消費されます。これにはデータやハードウェアリソースが含まれます。プロトタイプコピーを通じてこれらの消費を避けることができます。

(2)newでオブジェクトを作成する際に非常に複雑なデータ準備やアクセス権が必要な場合、プロトタイプモードを使用することができます。

(3)オブジェクトが他のオブジェクトにアクセスする必要があり、各呼び出し者が値を変更する可能性がある場合、プロトタイプモードを使用して複数のオブジェクトをコピーし、呼び出し者に使用する保護的なコピーを考慮することができます。

注意すべきことは、Cloneableインターフェースを実装したプロトタイプモードを通じてclone関数を呼び出してインスタンスを構築する場合、必ずしもnew操作よりも速くはならないことです。newでオブジェクトを構築するのが時間がかかるか、またはコストが高い場合にのみ、cloneメソッドを通じて効率の向上が得られます。したがって、Cloneableを使用する際には、オブジェクトの構築コストを考慮し、効率のテストを行う必要があります。もちろん、プロトタイプモードを実装するには、Cloneableインターフェースを実装する必要はありません。他の実装方法もあります。ここでは、これらを一つずつ説明します。

四、プロトタイプモデルのUMLクラス図

図中の役割紹介:

Client:クライアントユーザー。

Prototype:抽象クラスまたはインターフェース、clone能力を宣言。

ConcretePrototype:具体的なプロトタイプクラス。

五、プロトタイプモードのシンプルな実装

以下では、シンプルなドキュメントコピーを例にして、シンプルなプロトタイプモードを説明します。この例では、まずWordDocumentというドキュメントオブジェクトを作成し、テキストや画像が含まれています。ユーザーが長時間の内容編集を終えた後、このドキュメントに対してさらなる編集を行おうとしましたが、編集後のドキュメントが採用されるかどうかはまだ不確定です。そのため、安全のために、現在のドキュメントをコピーして、そのコピーに対して編集を行う必要がありました。これは、『Effective Java』の本で言及されている保護的なコピーに少し似ています。このように、この元のドキュメントは、私たちが言う「プロトタイプ」例であり、もっとも「クローン」されるべきオブジェクトです。私たちはそれを「プロトタイプ」と呼びます:

サンプルコード:

/**
 * ドキュメントのタイプ、ConcretePrototype役割を果たし、cloneableはPrototype役割を代表しています}}
 */
public class WordDocument implements Cloneable {
 //テキスト
 private String mText;
 //画像名リスト
 private ArrayList<String> mImages = new ArrayList<String>();
 public WordDocument(){
  System.out.println("-------- WordDocumentのコンストラクタ --------");
 }
 public String getText(){
  return this.mText;
 }
 public void setText(String text){
  this.mText = text;
 }
 public ArrayList<String> getImages(){
  return this.mImages;
 }
 public void setImages(ArrayList<String> images){
  this.mImages = images;
 }
 public void addImage(String img){
  this.mImages.add(img);
 }
 /**
  * ドキュメントを印刷
  */
 public void showDocument(){
  System.out.println("-------- Word Content Start --------");
  System.out.println("テキスト: " + this.mText);
  System.out.println("画像リスト: ");
  for(String image : mImages){
   System.out.println("画像名: " + image);
  }
  System.out.println("-------- Word Content End --------");
 }
 @Override
 protected WordDocument clone(){
  try{
   WordDocument doc = (WordDocument)super.clone();
   doc.mText = this.mText;
   doc.mImages = this.mImages;
   return doc;
  catch(Exception e){}
  return null;
 }
}

実行方法:

public static void main(String[] args) throws IOException {
  //1.ドキュメントオブジェクトの構築
  WordDocument originDoc = new WordDocument();
  //2.ドキュメントの編集、画像の追加など
  originDoc.setText("これはドキュメントです");
  originDoc.addImage("图片一");
  originDoc.addImage("图片二");
  originDoc.addImage("图片三");
  .addImage("これは新しく追加された画像です");
  //originDoc.addImage("画像三");
  元のドキュメントをプロトタイプとして、コピーを作成します2 WordDocument doc
  doc2originDoc.showDocument();
  //= originDoc.clone();
  doc2ドキュメントのコピーを修正します2.setText("これは修正されたDoc
  doc2テキスト");
  .addImage("これは新しく追加された画像です");
  doc2originDoc.showDocument();
}

.showDocument();

-------- WordDocumentのコンストラクタ --------
//originDoc
-------- Word Content Start --------
Text : これはドキュメントです
Images List :
image name : 画像1
image name : 画像2
image name : 画像3
-------- Word Content End --------
//doc2
-------- Word Content Start --------
Text : これはドキュメントです
Images List :
image name : 画像1
image name : 画像2
image name : 画像3
-------- Word Content End --------
//コピー後のoriginDoc
-------- Word Content Start --------
Text : これはドキュメントです
Images List :
image name : 画像1
image name : 画像2
image name : 画像3
image name : これは新しく追加された画像です
-------- Word Content End --------
//コピー後のドキュメント2
-------- Word Content Start --------
Text : これは修正されたDocです2テキスト
Images List :
image name : 画像1
image name : 画像2
image name : 画像3
image name : これは新しく追加された画像です
-------- Word Content End --------

実行結果:2ここで、doc

その後、originDocのmImagesに影響を与えただけで、mTextは変更されませんでした。

浅いコピーと深いコピー

上記のプロトタイプモードの実装は実際には浅いコピーであり、影響を受けませんでした。これは、元のドキュメントの全てのフィールドを再構築するのではなく、副本文書のフィールドが元のドキュメントのフィールドを参照することを示しています。以下の図のように:2細心の読者は上記の結果から、最後の2つのドキュメント情報の出力が一致することに気づくかもしれません。私たちはdoc++読者は非常に深い印象を持ちます。これは、上記のWordDocumentのcloneメソッドが単なる浅いコピーを行っていたためです。新しいdocオブジェクトに画像を追加しましたが、originDocにも表示されます。これはどういうことですか?C2doc.mImagesは単にthis.mImagesの参照を指しています。新しいmImagesオブジェクトを再構築し、元のドキュメントの画像を新しいmImagesオブジェクトに追加するのではなく、2doc.mImagesは元のドキュメントのオブジェクトと同じオブジェクトです。したがって、どちらかのドキュメントの画像を変更すると、もう一方のドキュメントも影響を受けます。この問題をどのように解決しますか?答えは深いコピーを用いることです。これは、オブジェクトをコピーする際に、参照型のフィールドもコピーの形式で、単なる参照の形式でないことです。

以下のようにcloneメソッドを修正しました(他は変更されていません):

@Override
protected WordDocument clone(){
  try{
   WordDocument doc = (WordDocument)super.clone();
   doc.mText = this.mText;
   //mImagesオブジェクトにもclone()関数を呼び出し、深いコピーを行います。
   doc.mImages = (ArrayList<String>)this.mImages.clone();
   return doc;
  catch(Exception e){}
  return null;
}

上記のコードを実行した後の結果は以下の通りです:

-------- WordDocumentのコンストラクタ --------
//originDoc
-------- Word Content Start --------
Text : これはドキュメントです
Images List :
image name : 画像1
image name : 画像2
image name : 画像3
-------- Word Content End --------
//doc2
-------- Word Content Start --------
Text : これはドキュメントです
Images List :
image name : 画像1
image name : 画像2
image name : 画像3
-------- Word Content End --------
//コピー後のoriginDoc
-------- Word Content Start --------
Text : これはドキュメントです
Images List :
image name : 画像1
image name : 画像2
image name : 画像3
-------- Word Content End --------
//コピー後のドキュメント2
-------- Word Content Start --------
Text : これは修正されたDocです2テキスト
Images List :
image name : 画像1
image name : 画像2
image name : 画像3
image name : これは新しく追加された画像です
-------- Word Content End --------

互いに影響を与えず、これは深いコピーと呼ばれます。

上記の疑問を続けて、実際にはStringデータ型は浅いコピーの際に参照データ型と同様に、独立してコピーされていません。同じアドレスを参照しています。Stringはcloneableインターフェースを実装していないため、参照のみコピーできます。(ここではソースコードを確認することができますが、ArrayListはcloneableインターフェースを実装しています。)しかし、その中の値を変更する場合、新しいメモリが割り当てられ、新しい値を保存するための新しいメモリ空間が確保されます。この参照は新しいメモリ空間を指し、元のStringはまだその参照を持っているため、回収されません。したがって、参照のコピーでありながら、値を変更する際にはコピーされたオブジェクトの値は変更されません。

したがって、多くの場合、Stringはcloneの際に基本データ型と同じように処理することができますが、equalsの際には注意が必要です。

プロトタイプモードは非常にシンプルなパターンであり、その核心問題は元のオブジェクトに対するコピーです。このパターンの使用過程で注意すべきポイントの一つは、深いコピー、浅いコピーの問題です。開発過程でエラーを減らすために、著者はこのパターンを使用する際には可能な限り深いコピーを使用し、元のオブジェクトに影響を与えないようにすることを推奨しています。

7、Androidのソースコード内のプロトタイプモード

サンプルコード:

Uri uri = Uri.parse("smsto:");110");
Intent intent = new Intent(Intent.ACTION_SEND,uri);
intent.putExtra("sms_body", "The SMS text");
//クローン
Intent intent2 = (Intent)intent.clone();
startActivity(intent2);

8、まとめ

プロトタイプモードは本質的にオブジェクトのコピーであり、C++クラスのコピー構築関数は似ていることが多く、その間で発生しやすい問題も深いコピー、浅いコピーです。プロトタイプモードを使用することで複雑なオブジェクトの構築におけるリソース消費問題を解決し、特定のシーンでオブジェクトの作成効率を向上させることができます。

長所:

(1)プロトタイプパターンはメモリ内のバイナリストリームのコピーであり、newでオブジェクトを構築するよりも多くの性能を提供します。特にループ内で大量のオブジェクトを生成する場合、プロトタイプパターンの利点がより明確になります。

(2)もう一つの重要な用途は保護的なコピーであり、読み取り専用のオブジェクトが外部から見える場合、この読み取り専用のオブジェクトが外部から変更されないようにするために、通常、オブジェクトのコピーを返す形で読み取り専用の制限を実現します。

短所:

(1)これはその長所でも短所でもあります。メモリ内でコピーすることで、コンストラクタは実行されません。実際の開発では、この潜在的な問題に注意する必要があります。長所は制約を減らすことですが、短所も制約を減らすことです。実際のアプリケーションにおける考慮が必要です。

(2)Cloneableインターフェースを実装したプロトタイプパターンでは、clone関数を呼び出してインスタンスを構築する場合、new操作よりも速くならないことがあります。newでオブジェクトを構築するのが時間がかかる場合やコストが高い場合に限り、cloneメソッドを通じて効率の向上が得られます。

Androidに関するさらに詳しい内容に興味がある読者は、以下の本サイトの特集をチェックしてください:《Android開発入門と進階教程》、《Androidデバッグ技術とよくある問題の解決方法まとめ》、《Android基本コンポーネントの使い方まとめ》、《AndroidビューViewの技術まとめ》、《Androidレイアウトlayoutの技術まとめ》および《Androidコントロールの使い方まとめ》

この記事の内容が皆様のAndroidプログラム設計に役立つことを願っています。

声明:この記事の内容はインターネットから取得しており、著作権者に帰属します。インターネットユーザーが自発的に提供し、自己でアップロードしたものであり、このサイトは所有権を有しないです。また、人工的な編集は行われていませんし、関連する法的責任も負いません。著作権侵害が疑われる内容を見つけた場合は、以下のメールアドレスにご連絡ください:notice#oldtoolbag.com(メール送信時は、#を@に変更してください。報告を行い、関連する証拠を提供してください。一旦確認が取れたら、このサイトは即座に侵害疑いのコンテンツを削除します。)

おすすめ