设计模式——原型模式代码示例

引言

原型是一种创建型设计模式, 使你能够复制对象, 甚至是复杂对象, 而又无需使代码依赖它们所属的类。

所有的原型类都必须有一个通用的接口, 使得即使在对象所属的具体类未知的情况下也能复制对象。 原型对象可以生成自身的完整副本, 因为相同类的对象可以相互访问对方的私有成员变量。

Java代码示例 

使用示例: Java 的 Cloneable  (可克隆) 接口就是立即可用的原型模式。

任何类都可通过实现该接口来实现可被克隆的性质。

java.lang.Object#clone() (类必须实现 java.lang.Cloneable 接口)

识别方法: 原型可以简单地通过 clone或 copy等方法来识别。

复制图形

让我们来看看在不使用标准 Cloneable接口的情况下如何实现原型模式。

shapes: 形状列表

 shapes/Shape.java: 通用形状接口
import java.util.Objects;public abstract class Shape {public int x;public int y;public String color;public Shape() {}public Shape(Shape target) {if (target != null) {this.x = target.x;this.y = target.y;this.color = target.color;}}public abstract Shape clone();@Overridepublic boolean equals(Object object2) {if (!(object2 instanceof Shape)) return false;Shape shape2 = (Shape) object2;return shape2.x == x && shape2.y == y && Objects.equals(shape2.color, color);}
}

 shapes/Circle.java: 简单形状

public class Circle extends Shape {public int radius;public Circle() {}public Circle(Circle target) {super(target);if (target != null) {this.radius = target.radius;}}@Overridepublic Shape clone() {return new Circle(this);}@Overridepublic boolean equals(Object object2) {if (!(object2 instanceof Circle) || !super.equals(object2)) return false;Circle shape2 = (Circle) object2;return shape2.radius == radius;}
}

 shapes/Rectangle.java: 另一个形状

public class Rectangle extends Shape {public int width;public int height;public Rectangle() {}public Rectangle(Rectangle target) {super(target);if (target != null) {this.width = target.width;this.height = target.height;}}@Overridepublic Shape clone() {return new Rectangle(this);}@Overridepublic boolean equals(Object object2) {if (!(object2 instanceof Rectangle) || !super.equals(object2)) return false;Rectangle shape2 = (Rectangle) object2;return shape2.width == width && shape2.height == height;}
}
 Demo.java: 克隆示例
import refactoring_guru.prototype.example.shapes.Circle;
import refactoring_guru.prototype.example.shapes.Rectangle;
import refactoring_guru.prototype.example.shapes.Shape;import java.util.ArrayList;
import java.util.List;public class Demo {public static void main(String[] args) {List<Shape> shapes = new ArrayList<>();List<Shape> shapesCopy = new ArrayList<>();Circle circle = new Circle();circle.x = 10;circle.y = 20;circle.radius = 15;circle.color = "red";shapes.add(circle);Circle anotherCircle = (Circle) circle.clone();shapes.add(anotherCircle);Rectangle rectangle = new Rectangle();rectangle.width = 10;rectangle.height = 20;rectangle.color = "blue";shapes.add(rectangle);cloneAndCompare(shapes, shapesCopy);}private static void cloneAndCompare(List<Shape> shapes, List<Shape> shapesCopy) {for (Shape shape : shapes) {shapesCopy.add(shape.clone());}for (int i = 0; i < shapes.size(); i++) {if (shapes.get(i) != shapesCopy.get(i)) {System.out.println(i + ": Shapes are different objects (yay!)");if (shapes.get(i).equals(shapesCopy.get(i))) {System.out.println(i + ": And they are identical (yay!)");} else {System.out.println(i + ": But they are not identical (booo!)");}} else {System.out.println(i + ": Shape objects are the same (booo!)");}}}
}
OutputDemo.txt: 执行结果
0: Shapes are different objects (yay!)
0: And they are identical (yay!)
1: Shapes are different objects (yay!)
1: And they are identical (yay!)
2: Shapes are different objects (yay!)
2: And they are identical (yay!)

原型注册站

你可以实现中心化的原型注册站 (或工厂), 其中包含一系列预定义的原型对象。 这样一来, 你就可以通过传递对象名称或其他参数的方式从工厂处获得新的对象。 工厂将搜索合适的原型, 然后对其进行克隆复制, 最后将副本返回给你。

cache

 cache/BundledShapeCache.java: 原型工厂
import refactoring_guru.prototype.example.shapes.Circle;
import refactoring_guru.prototype.example.shapes.Rectangle;
import refactoring_guru.prototype.example.shapes.Shape;import java.util.HashMap;
import java.util.Map;public class BundledShapeCache {private Map<String, Shape> cache = new HashMap<>();public BundledShapeCache() {Circle circle = new Circle();circle.x = 5;circle.y = 7;circle.radius = 45;circle.color = "Green";Rectangle rectangle = new Rectangle();rectangle.x = 6;rectangle.y = 9;rectangle.width = 8;rectangle.height = 10;rectangle.color = "Blue";cache.put("Big green circle", circle);cache.put("Medium blue rectangle", rectangle);}public Shape put(String key, Shape shape) {cache.put(key, shape);return shape;}public Shape get(String key) {return cache.get(key).clone();}
}

 Demo.java: 克隆示例

import refactoring_guru.prototype.caching.cache.BundledShapeCache;
import refactoring_guru.prototype.example.shapes.Shape;public class Demo {public static void main(String[] args) {BundledShapeCache cache = new BundledShapeCache();Shape shape1 = cache.get("Big green circle");Shape shape2 = cache.get("Medium blue rectangle");Shape shape3 = cache.get("Medium blue rectangle");if (shape1 != shape2 && !shape1.equals(shape2)) {System.out.println("Big green circle != Medium blue rectangle (yay!)");} else {System.out.println("Big green circle == Medium blue rectangle (booo!)");}if (shape2 != shape3) {System.out.println("Medium blue rectangles are two different objects (yay!)");if (shape2.equals(shape3)) {System.out.println("And they are identical (yay!)");} else {System.out.println("But they are not identical (booo!)");}} else {System.out.println("Rectangle objects are the same (booo!)");}}
}
OutputDemo.txt: 执行结果
Big green circle != Medium blue rectangle (yay!)
Medium blue rectangles are two different objects (yay!)
And they are identical (yay!)

GO语言代码示例

概念示例

让我们尝试通过基于操作系统文件系统的示例来理解原型模式。 操作系统的文件系统是递归的: 文件夹中包含文件和文件夹, 其中又包含文件和文件夹, 以此类推。

每个文件和文件夹都可用一个 inode接口来表示。 ​ inode接口中同样也有 clone克隆功能。

file文件和 folder文件夹结构体都实现了 print打印和 clone方法, 因为它们都是 inode类型。 同时, 注意 file和 folder中的 clone方法。 这两者的 clone方法都会返回相应文件或文件夹的副本。 同时在克隆过程中, 我们会在其名称后面添加 “_clone” 字样。

 inode.go: 原型接口
package maintype Inode interface {print(string)clone() Inode
}
 file.go: 具体原型
package mainimport "fmt"type File struct {name string
}func (f *File) print(indentation string) {fmt.Println(indentation + f.name)
}func (f *File) clone() Inode {return &File{name: f.name + "_clone"}
}
 folder.go: 具体原型
package mainimport "fmt"type Folder struct {children []Inodename     string
}func (f *Folder) print(indentation string) {fmt.Println(indentation + f.name)for _, i := range f.children {i.print(indentation + indentation)}
}func (f *Folder) clone() Inode {cloneFolder := &Folder{name: f.name + "_clone"}var tempChildren []Inodefor _, i := range f.children {copy := i.clone()tempChildren = append(tempChildren, copy)}cloneFolder.children = tempChildrenreturn cloneFolder
}
 main.go: 客户端代码
package mainimport "fmt"func main() {file1 := &File{name: "File1"}file2 := &File{name: "File2"}file3 := &File{name: "File3"}folder1 := &Folder{children: []Inode{file1},name:     "Folder1",}folder2 := &Folder{children: []Inode{folder1, file2, file3},name:     "Folder2",}fmt.Println("\nPrinting hierarchy for Folder2")folder2.print("  ")cloneFolder := folder2.clone()fmt.Println("\nPrinting hierarchy for clone Folder")cloneFolder.print("  ")
}
 output.txt: 执行结果
Printing hierarchy for Folder2Folder2Folder1File1File2File3Printing hierarchy for clone FolderFolder2_cloneFolder1_cloneFile1_cloneFile2_cloneFile3_clone

Ruby代码示例

main.rb: 概念示例
# The example class that has cloning ability. We'll see how the values of field
# with different types will be cloned.
class Prototypeattr_accessor :primitive, :component, :circular_referencedef initialize@primitive = nil@component = nil@circular_reference = nilend# @return [Prototype]def clone@component = deep_copy(@component)# Cloning an object that has a nested object with backreference requires# special treatment. After the cloning is completed, the nested object# should point to the cloned object, instead of the original object.@circular_reference = deep_copy(@circular_reference)@circular_reference.prototype = selfdeep_copy(self)end# deep_copy is the usual Marshalling hack to make a deep copy. But it's rather# slow and inefficient, therefore, in real applications, use a special gemprivate def deep_copy(object)Marshal.load(Marshal.dump(object))end
endclass ComponentWithBackReferenceattr_accessor :prototype# @param [Prototype] prototypedef initialize(prototype)@prototype = prototypeend
end# The client code.
p1 = Prototype.new
p1.primitive = 245
p1.component = Time.now
p1.circular_reference = ComponentWithBackReference.new(p1)p2 = p1.cloneif p1.primitive == p2.primitiveputs 'Primitive field values have been carried over to a clone. Yay!'
elseputs 'Primitive field values have not been copied. Booo!'
endif p1.component.equal?(p2.component)puts 'Simple component has not been cloned. Booo!'
elseputs 'Simple component has been cloned. Yay!'
endif p1.circular_reference.equal?(p2.circular_reference)puts 'Component with back reference has not been cloned. Booo!'
elseputs 'Component with back reference has been cloned. Yay!'
endif p1.circular_reference.prototype.equal?(p2.circular_reference.prototype)print 'Component with back reference is linked to original object. Booo!'
elseprint 'Component with back reference is linked to the clone. Yay!'
end
 output.txt: 执行结果
Primitive field values have been carried over to a clone. Yay!
Simple component has been cloned. Yay!
Component with back reference has been cloned. Yay!
Component with back reference is linked to the clone. Yay!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/219271.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C 标准库 - <time.h>

简介 time.h 头文件定义了四个变量类型、两个宏和各种操作日期和时间的函数。 库变量 下面是头文件 time.h 中定义的变量类型&#xff1a; 序号变量 & 描述1size_t是无符号整数类型&#xff0c;它是 sizeof 关键字的结果。2clock_t这是一个适合存储处理器时间的类型。3…

Linux的五种IO模型

众所周知&#xff0c;出于对 OS 安全性的考虑&#xff0c;用户进程是不能直接操作 I/O 设备的。必须通过系统调用请求操作系统内核来协助完成 I/O 动作。 下图展示了 Linux I/O 的过程。 操作系统内核收到用户进程发起的请求后&#xff0c;从 I/O 设备读取数据到 kernel buff…

【超详细】创建vue3+ts项目(引入ElementPlus、Axios)

目录 前言1、使用vue脚手架创建项目1.1检查vue版本1.2 使用vue脚手架创建项目 2、删除项目多余文件&#xff0c;修改配置项目2.1、删除以下文件2.1、在views下创建index文件2.2、修改router/index.ts路由文件&#xff1a;2.3、修改App.vue文件&#xff1a;2.4、初始化页面样式以…

LeetCode141. Linked List Cycle

文章目录 一、题目二、题解 一、题目 Given head, the head of a linked list, determine if the linked list has a cycle in it. There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next poi…

windows上抓包出现大包未分片以及关闭tso方法

wireshark抓包中会有大数据包&#xff08;未分片包&#xff09;和ip校验和不对的包&#xff0c;问题根因在目前很多电脑网卡支持TSO和将校验和计算到网卡上&#xff0c;导致抓出数据包未分片 详细文章看: https://www.cnblogs.com/charlieroro/p/11363336.html 目前很多网卡已…

业务代码-整合框架-存储-缓存常见错误详解一

一. java空指针和异常&#xff1a; 1.什么是空指针异常&#xff08;java.lang.NullPointException)&#xff1a; 1.1常见的空指针异常案例&#xff1a; public class WhatIsNpe {public static class User {private String name;private String[] address;public void print…

项目一 分析并设计学生管理数据库

项目一 分析并设计学生管理数据库 1&#xff0c;做好管理数据库的知识准备 1.1&#xff0c;初识数据库 **1&#xff0c;DBMS&#xff1a;**数据库管理系统(Database Management System)。数据库 是通过DBMS创建和操作的 容器。 **2&#xff0c;DB&#xff1a;**数据库(data…

Python入门第2篇(pip、字符串、方法、json、io操作)

目录 pip包管理器 字符串 方法 json 文件操作 pip包管理器 包管理器类似.NET下的nuget&#xff0c;主要用于管理引用依赖项。 安装Python的时候&#xff0c;已经默认安装了pip包管理器&#xff0c;因此无需单独安装 cmd&#xff0c;输入&#xff1a;pip --version 显示…

C语言:高精度加法

P1601 AB Problem&#xff08;高精&#xff09; - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 过大的数计算就无法用变量定义计算&#xff0c;但可以用数组巧妙的化解这个问题。 #include<stdio.h> #include<stdlib.h> #include<string.h> char x[10005];…

004 Windows NTFS文件夹权限

一、NTFS文件权限&#xff1a; NTFS&#xff08;New Technology File System&#xff09;是Windows NT内核的系列操作系统支持的、一个特别为网络和磁盘配额、文件加密等管理安全特性设计的磁盘格式&#xff0c;提供长文件名、数据保护和恢复&#xff0c;能通过目录和文件许可…

ffmpeg编解码——数据包(packet)概念(如何正确处理数据包中的显示时间戳pts与解码时间戳dts关系?)

文章目录 FFmpeg编解码——数据包&#xff08;Packet&#xff09;概念1. 数据包&#xff08;Packet&#xff09;简介2. 数据包&#xff08;Packet&#xff09;在FFmpeg中的应用2.1 从媒体文件读取数据包2.2 向媒体文件写入数据包 3. 数据包&#xff08;Packet&#xff09;相关问…

【Docker】学习笔记(三)三剑客之 docker-compose文件书写项目多服务容器运行

简介 引言&#xff08;需求&#xff09; 为了完成一个完整项目势必用到N多个容器配合完成项目中的业务开发&#xff0c;一旦引入N多个容器&#xff0c;N个容器之间就会形成某种依赖&#xff0c;也就意味着某个容器的运行需要其他容器优先启动之后才能正常运行&#xff1b; 容…

策略模式实现

策略模式: 策略模式是一种行为型设计模式&#xff0c;它允许你定义一系列算法&#xff0c;把它们封装起来&#xff0c;并且使它们可以互相替换。这样&#xff0c;使用算法的客户端代码可以独立于具体的算法实现方式。 就好像是你要去旅行&#xff0c;你可以选择多种不同的交通…

【EI会议征稿中|IEEE出版】第三届信息技术与当代体育国际学术会议(TCS 2023)

【IEEE出版】第三届信息技术与当代体育国际学术会议&#xff08;TCS 2023&#xff09; 2023 3rd International Conference on Information Technology and Contemporary Sports 2023年第三届信息技术与当代体育国际学术会议&#xff08;TCS 2023&#xff09;将于2023年12月2…

Dueling DQN 跑 Pendulum-v1

gym-0.26.1 Pendulum-v1 Dueling DQN 因为还是DQN,所以我们沿用double DQN,然后把 Qnet 换成 VAnet。 其他的不变&#xff0c;详情参考前一篇文章。 class VA(nn.Module):"""只有一层隐藏层的A网络和V网络"""def __init__(self, state_dim, hidd…

子目录文件夹图片汇总

import os import shutildef collect_images(source_folder, target_folder):# 遍历主文件夹及其所有子文件夹for root, dirs, files in

位1的个数

题目链接 位1的个数 题目描述 注意点 输入必须是长度为 32 的 二进制串 解答思路 位运算判断每一位是否为1 代码 public class Solution {// you need to treat n as an unsigned valuepublic int hammingWeight(int n) {int res 0;for (int i 0; i < 32; i) {res …

项目经理和产品经理该如何选择?

最近很多人咨询“项目经理跟产品经理该怎么选&#xff0c;我更适合哪个&#xff1f;”“项目经理跟产品经理哪个更有钱途 ”“项目经理转产品经理好转吗”等等&#xff0c;今天就一次性说清楚项目经理跟产品经理有什么区别&#xff0c;应该怎么选择。 不想看长篇大论的&#x…

Python+Pytest接口自动化之HTTP协议基础

HTTP协议简介 HTTP 即 HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09;&#xff0c;是互联网上应用最为广泛的一种网络协议。所有的 WWW 文件都必须遵守这个标准。 设计 HTTP 最初的目的是为了提供一种发布和接收 HTML 页面的方法。HTTP 协议在 OSI 模型…

Kubernetes版本升级到v1.18.0方法

升级k8s版本才能使用kube-prometheus安装监控 1、查看集群状态 [rootk8s-master k8s-script]# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready master 5d22h v1.18.0 k8s-slave1 Ready <none> 4d10h v1.18.0 k…