English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
이 문서는 Android 프로그래밍 디자인 패턴 중 프로토타입 패턴을 설명합니다. 여러분께 공유하고, 구체적으로 다음과 같습니다:
1. 소개
프로토타입 패턴은 생성형 패턴입니다. '프로토타입'이라는 단어는 이 모델이 템플릿 인스턴스를 가져야 한다는 것을 의미합니다. 사용자는 이 템플릿 객체에서 내부 속성이 일치하는 객체를 복사하여 생성합니다. 이 과정은 우리가 일반적으로 '클론'이라고 부르는 것입니다. 복사된 인스턴스는 우리가 '프로토타입'이라고 부르며, 이 프로토타입도 정제될 수 있습니다. 프로토타입 모델은 복잡하거나 생성이 비용이 많이 드는 경우에 많이 사용되며, 이 경우에 이미 존재하는 인스턴스를 복사하여 프로그램이 더 효율적으로 실행될 수 있습니다.
2. 정의
프로토타입 인스턴스를 사용하여 객체의 종류를 지정하고, 이러한 프로토타입을 복사하여 새로운 객체를 생성합니다.
3. 사용 시나리오
(1)클래스 초기화가 매우 많은 자원을 소모할 때, 이 자원은 데이터, 하드웨어 자원 등을 포함합니다. 이를 통해 프로토타입 복사를 통해 이러한 소모를 피할 수 있습니다.
(2)new 연산자를 통해 객체를 생성하는 데 매우 복잡한 데이터 준비나 접근 권한이 필요할 때, 프로토타입 패턴을 사용할 수 있습니다.
(3)한 객체가 다른 객체에 접근할 필요가 있으며, 각 호출자가 그 값을 수정할 가능성이 있을 때, 프로토타입 패턴을 사용하여 여러 개의 객체를 호출자에게 제공할 수 있습니다. 이를 보호적 복사라고 합니다.
Cloneable 인터페이스를 통해 구현된 프로토타입 패턴은 clone 함수를 호출하여 인스턴스를 생성할 때 반드시 new 연산자보다 빠르지 않을 수 있습니다. new 연산자를 통해 객체를 생성하는 데 시간이 많이 소요되거나 비용이 높을 때만 clone 메서드를 통해 효율성을 높일 수 있습니다. 따라서 Cloneable을 사용할 때는 객체 생성 비용을 고려하고 효율성 테스트를 수행해야 합니다. 물론, 프로토타입 패턴을 구현할 때는 반드시 Cloneable 인터페이스를 구현할 필요는 없으며, 다른 구현 방법도 있습니다. 여기서는 이러한 것들을 하나씩 설명할 것입니다.
4. 프로토타입 모델의 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("Text : " + this.mText); System.out.println("Images List : "); for(String image : mImages){ System.out.println("image name : " + 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("图片三"); originDoc.showDocument(); //을 원본 문서로부터 복사하여 한 복사본을 만듭니다 WordDocument doc2 = originDoc.clone(); doc2.showDocument(); //문서 복사본 doc2.setText("这是修改过的Doc2文本"); doc2.addImage("이是新添加的图片"); originDoc.showDocument(); doc2.showDocument(); }
의 실행 결과는 다음과 같습니다:
-------- WordDocument 생성자 -------- //originDoc -------- Word Content Start -------- 텍스트: 이 문서 이미지 목록: 이미지 이름: 이미지 1 이미지 이름: 이미지 2 이미지 이름: 이미지 3 -------- Word Content End -------- //doc2 -------- Word Content Start -------- 텍스트: 이 문서 이미지 목록: 이미지 이름: 이미지 1 이미지 이름: 이미지 2 이미지 이름: 이미지 3 -------- Word Content End -------- //복사본 수정 후 originDoc -------- Word Content Start -------- 텍스트: 이 문서 이미지 목록: 이미지 이름: 이미지 1 이미지 이름: 이미지 2 이미지 이름: 이미지 3 이미지 이름: 새로 추가된 이미지 -------- Word Content End -------- //복사본 수정 후 doc2 -------- Word Content Start -------- 텍스트: 수정된 Doc2텍스트 이미지 목록: 이미지 이름: 이미지 1 이미지 이름: 이미지 2 이미지 이름: 이미지 3 이미지 이름: 새로 추가된 이미지 -------- Word Content End --------
여기서 우리는 doc2이후 originDoc의 mImages에 영향을 미쳤지만, mText는 변경되지 않았습니다.
6. 얕은 복사와 깊은 복사
위의 원형 모델 구현은 실제로는 얕은 복사입니다. 이 복사는 원본 문서의 모든 필드를 다시 구성하는 것이 아니라, 부본 문서의 필드가 원본 문서의 필드를 참조하는 것입니다. 아래 그림과 같이 나타냅니다:
주의 깊은 독자는 위의 결과에서 마지막 두 개의 문서 정보 출력이 일치한다는 것을 발견할 수 있을 것입니다. 우리는 doc2에 이미지를 추가했지만, 동시에 originDoc에도 표시되는 이유는 무엇인가요? C를 배운 사람이라면,++의 독자 모두가 매우 깊은 인상을 받을 것입니다. 이는 WordDocument의 clone 메서드에서 단순히 얕은 복사를 수행했기 때문입니다. 새로운 doc2.mImages는 단순히 this.mImages 참조를 가리키고 있으며, 새로운 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(); return doc; }catch(Exception e){} return null; }
수정된 후에 위의 코드를 실행한 결과는 다음과 같습니다:
-------- WordDocument 생성자 -------- //originDoc -------- Word Content Start -------- 텍스트: 이 문서 이미지 목록: 이미지 이름: 이미지 1 이미지 이름: 이미지 2 이미지 이름: 이미지 3 -------- Word Content End -------- //doc2 -------- Word Content Start -------- 텍스트: 이 문서 이미지 목록: 이미지 이름: 이미지 1 이미지 이름: 이미지 2 이미지 이름: 이미지 3 -------- Word Content End -------- //복사본 수정 후 originDoc -------- Word Content Start -------- 텍스트: 이 문서 이미지 목록: 이미지 이름: 이미지 1 이미지 이름: 이미지 2 이미지 이름: 이미지 3 -------- Word Content End -------- //복사본 수정 후 doc2 -------- Word Content Start -------- 텍스트: 수정된 Doc2텍스트 이미지 목록: 이미지 이름: 이미지 1 이미지 이름: 이미지 2 이미지 이름: 이미지 3 이미지 이름: 새로 추가된 이미지 -------- 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)이는 그의 장점과 단점입니다. 메모리에서 직접 복사하면, 생성자는 실행되지 않습니다. 실제 개발에서는 이러한 잠재적인 문제를 주의해야 합니다. 장점은 제약을 줄임으로써, 단점도 제약을 줄임으로써, 실제 응용에서 고려해야 합니다.
(2Cloneable 인터페이스를 구현한 프로토타입 패턴은 clone 함수를 호출하여 인스턴스를 생성할 때 반드시 new 연산자보다 빠르지 않을 수 있습니다. new 연산자를 통해 객체를 생성하는 데 시간이 많이 걸리거나 비용이 높을 때만 clone 메서드를 통해 성능 향상을 얻을 수 있습니다.
Android와 관련된 내용에 대해 더 알고 싶은 독자는 다음 사이트의 특辑을 확인할 수 있습니다.:《Android 개발 입문 및 고급 강의》、《Android 디버깅 기술 및 일반 문제 해결 방법 요약》、《Android 기본 구성 요소 사용 방법 요약》、《Android 뷰 View 기술 요약》、《Android 레이아웃 Layout 기술 요약》 및 《Android 컨트롤러 사용 방법 요약》
본 문서에서 설명된 내용이 모두 여러분의 Android 프로그램 설계에 도움이 되길 바랍니다.
성명: 본문은 인터넷에서 가져왔으며, 저작권자가 소유하고 있으며, 인터넷 사용자가 자발적으로 기여하고 업로드한 내용입니다. 사이트는 소유권을 가지지 않으며, 인공 편집을 하지 않았으며, 관련 법적 책임을 부담하지 않습니다. 저작권 침해 내용이 있음을 발견하면, notice#w로 이메일을 보내 주시기 바랍니다.3codebox.com에 대한 신고를 보내는 경우, #을 @으로 변경하십시오. 관련 증거를 제공하고, 사실이 확인되면, 사이트는 즉시 저작권 침해 내용을 삭제합니다.