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 --------");
  System.out.println("Текст : " + this.mText);
  System.out.println("Список изображений : ");
  for(String image : mImages){
   System.out.println("имя изображения : " + image);
  }
  System.out.println("-------- Конец содержимого документа Word --------");
 }
 @Override
 protected WordDocument clone(){
  try{
   WordDocument doc = (WordDocument)super.clone();
   doc.mText = this.mText;
   doc.mImages = this.mImages;
   возвратить doc;
  catch(Exception e){}
  возвратить null;
 }
}

Выполнить метод:

public static void main(String[] args) throws IOException {
  // 1. Создаем объект документа
  WordDocument originDoc = new WordDocument();
  // 2. Редактируем документ, добавляем изображения и т.д.
  originDoc.setText("这是一篇文档");
  originDoc.addImage("图片一");
  originDoc.addImage("图片二");
  originDoc.addImage("图片三");
  originDoc.showDocument();
  // с помощью исходного документа как прототипа копируем его副本
  WordDocument doc2 = originDoc.clone();
  doc2.showDocument();
  //Изменение копии документа
  doc2.setText("Это измененный текст Doc2");
  doc2.addImage("Это добавленное изображение");
  originDoc.showDocument();
  doc2.showDocument();
}

Результат выполнения:

-------- Конструктор WordDocument --------
//originDoc
-------- Начало содержимого Word --------
Текст: Это документ
Список изображений:
имя изображения: Изображение одно
имя изображения: Изображение два
имя изображения: Изображение три
-------- Конец содержимого Word --------
//doc2
-------- Начало содержимого Word --------
Текст: Это документ
Список изображений:
имя изображения: Изображение одно
имя изображения: Изображение два
имя изображения: Изображение три
-------- Конец содержимого Word --------
//изменен после копирования originDoc
-------- Начало содержимого Word --------
Текст: Это документ
Список изображений:
имя изображения: Изображение одно
имя изображения: Изображение два
имя изображения: Изображение три
имя изображения: Это новое добавленное изображение
-------- Конец содержимого Word --------
//изменен после копирования doc2
-------- Начало содержимого Word --------
Текст: Это измененный текст Doc2
Список изображений:
имя изображения: Изображение одно
имя изображения: Изображение два
имя изображения: Изображение три
имя изображения: Это новое добавленное изображение
-------- Конец содержимого Word --------

Мы обнаружили, что после изменения doc2, это только повлияло на mImages в originDoc, а не изменило mText.

Шестой раздел: поверхностное и глубокое копирование

Реализация прототипа на самом деле является поверхностным копированием, также известным как тень копирование. Это копирование не создает новый документ, а просто ссылается на поля исходного документа, как показано на рисунке:

Внимательные читатели могут заметить, что последние две информации о документах одинаковые. Мы добавили изображение в doc2, но оно также отображается в originDoc. Что это за ситуация? Читатели, изучавшие C++, имеют глубокое понимание этого. Это связано с тем, что в методе clone класса WordDocument был выполнен только поверхностный копир, объект нового типа doc2.mImages просто указывал на ссылку this.mImages, а не создавал новый объект mImages, в который бы добавлялись изображения из исходного документа. Это привело к тому, что doc2.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();
   возвратить doc;
  catch(Exception e){}
  возвратить null;
}

Результат выполнения кода после изменений:

-------- Конструктор WordDocument --------
//originDoc
-------- Начало содержимого Word --------
Текст: Это документ
Список изображений:
имя изображения: Изображение одно
имя изображения: Изображение два
имя изображения: Изображение три
-------- Конец содержимого Word --------
//doc2
-------- Начало содержимого Word --------
Текст: Это документ
Список изображений:
имя изображения: Изображение одно
имя изображения: Изображение два
имя изображения: Изображение три
-------- Конец содержимого Word --------
//изменен после копирования originDoc
-------- Начало содержимого Word --------
Текст: Это документ
Список изображений:
имя изображения: Изображение одно
имя изображения: Изображение два
имя изображения: Изображение три
-------- Конец содержимого Word --------
//изменен после копирования doc2
-------- Начало содержимого Word --------
Текст: Это измененный текст Doc2
Список изображений:
имя изображения: Изображение одно
имя изображения: Изображение два
имя изображения: Изображение три
имя изображения: Это новое добавленное изображение
-------- Конец содержимого Word --------

Таким образом, они не влияют друг на друга, это называется глубоким копированием.

Продолжая上面的 вопрос, тип String при неглубоком копировании behaves как тип ссылок, не создается отдельная копия, а используется один и тот же адрес, так как String не реализует интерфейс cloneable, что означает, что можно только копировать ссылку (мы можем увидеть это в исходном коде, а ArrayList реализует интерфейс cloneable). Но при изменении одного из значений создается новый блок памяти для сохранения нового значения, ссылка указывает на этот новый блок памяти, а исходный String, так как существует ссылка на него, не будет回收лен. Таким образом,虽然是 копируемая ссылка, но при изменении значения не изменяется значение копируемого объекта.

Таким образом, в большинстве случаев можно обрабатывать String так же, как и базовые типы при клонировании, но при этом нужно обратить внимание на equals.

Модель прототипа очень проста, ее ядро - это копирование исходного объекта. Один из моментов, на который нужно обратить внимание при использовании этой модели, это проблема глубокого и неглубокого копирования. В процессе разработки для уменьшения ошибок автор рекомендует использовать глубокое копирование, чтобы избежать проблем с влиянием на исходный объект при работе с копией.

Седьмое: модель прототипа в исходном коде 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);

Восьмое: резюме

Принцип прототипа заключается в копировании объектов, что несколько напоминает копирующий конструктор в C++, и проблемы, с которыми они сталкиваются, также связаны с глубоким и неглубоким копированием. Применение модели прототипа может решить проблему потребления ресурсов при создании сложных объектов и может повысить эффективность создания объектов в некоторых ситуациях.

Преимущества:

(1) Прототип - это копия двоичного потока в памяти, что значительно лучше, чем создание нового объекта через new, особенно когда необходимо создать большое количество объектов в цикле, прототип может лучше проявить свои преимущества.

(2) Одна из других важных функций - это копирование для защиты, то есть объект, который может быть только чтением снаружи, чтобы предотвратить модификацию этого только чтения объекта внешними, обычно можно реализовать ограничение только чтения, возвращая копию объекта.

Недостатки:

(1) Это и его优点, и недостатки, копирование в памяти, конструктор не выполняется, в реальном разработке необходимо учитывать этот потенциальный вопрос. Преимущество - это уменьшение ограничений, недостаток также уменьшение ограничений, необходимо учитывать при использовании в реальных приложениях.

(2) При реализации прототипа через интерфейс Cloneable при вызове функции clone, создание экземпляра не всегда быстрее, чем при использовании оператора new, только когда создание объекта через new занимает много времени или требует больших затрат, через метод clone можно получить улучшение производительности.

Читатели, заинтересованные в дополнительной информации о Android, могут ознакомиться с темами сайта: «Введение в разработку Android и tuorial по продвинутым навыкам», «Советы по отладке и решения проблем Android», «Обзор основных компонентов Android», «Советы по работе с Android View View», «Советы по работе с Android layout layout» и «Обзор использования Android控件»

Надеюсь, что информация, изложенная в этой статье, поможет вам в разработке Android-приложений.

Заявление: содержание этой статьи взято из Интернета, авторские права принадлежат их законным владельцам, материалы предоставлены пользователями Интернета, сайт не имеет права собственности, материалы не были отредактированы вручную, и сайт не несет ответственности за соответствующие юридические последствия. Если вы обнаружите контент,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма замените # на @) для сообщения о нарушении,并提供 соответствующие доказательства. Если факт будет подтвержден, сайт немедленно удалит涉嫌侵权的内容.

Основной учебник
Рекомендуем