Maison  >  Article  >  Java  >  Mode prototype maître en cinq minutes

Mode prototype maître en cinq minutes

Java后端技术全栈
Java后端技术全栈avant
2023-08-25 15:52:43950parcourir


Bonjour à tous, je suis Lao Tian, ​​​​Aujourd'hui, je vais partager avec vous 原型模式 des modèles de conception. Utilisez des histoires de vie appropriées et des scénarios de projet réels pour parler du modèle de conception, et enfin résumez le modèle de conception en une phrase.

Mode prototype maître en cinq minutes

Histoire

Je me souviens encore que lorsque je cherchais un emploi au cours de ma dernière année, j'ai accidentellement trouvé un modèle de CV de programmeur relativement beau sur Internet, puis toute la classe a commencé à le copier leurs CV comme des fous (disque U). Dans le même temps, il y a eu aussi une blague. Plusieurs étudiants ont copié leurs curriculum vitae sans changer le contenu ni leur nom. Ils les ont soumis à l'intervieweur (intervieweur de recrutement sur le campus) comme date limite. Plus tard, tout le monde devrait pouvoir deviner le résultat. Tout le monde est parti en stage, et certains d'entre eux étaient encore à la recherche d'un emploi. Retour de l'intervieweur de l'entreprise et d'autres camarades de classe plus tard : j'ai reçu plusieurs CV qui étaient exactement les mêmes. À mon retour, tout le monde savait quel était le problème après en avoir parlé. J'ai admis que je l'avais copié et soumis. sans le changer du tout. Quel dommage.

Divisez les copies de CV en deux types :

  • La première consiste à copier le CV puis à modifier les informations selon les vôtres
  • L'autre consiste à copier le CV sans changer le contenu.

Définition du modèle de prototype

Spécifiez les types d'objets à créer à l'aide d'une instance de prototype et créez de nouveaux objets en faisant face à ce prototype

signifiant grosso modo : Spécifiez la création d'objets avec un type d'instance de prototype et créez de nouveaux objets en copiant ces prototypes.

Mode Prototype : Prototype Pattern, un mode créatif.

L'appelant n'a pas besoin de connaître les détails de la création, ni d'appeler le constructeur pour créer l'objet.

Scénarios d'utilisation

Le mode prototype propose les scénarios d'utilisation suivants :

  • L'initialisation de la classe consomme plus de ressources
  • Un objet généré par new nécessite un processus très fastidieux (préparation des données, autorisations d'accès, etc.)
  • Le constructeur est plus compliqué
  • Quand un grand nombre de les objets sont générés dans le corps de la boucle
  • Au Spring, le modèle prototype est largement utilisé, par exemple : scope='prototype'scope='prototype'

我们可以将一些getter和setter之类封装成一个工厂方法,然后对于使用的人来说,调用方法就可以了,不需要知道里面的getter和setter是怎么处理的。我们也可以使用JDK提供的实现Cloneable接口,实现快速复制。

创建对象的四种方式:

new、反射、克隆、序列化

实际案例

大家是否有遇到过这种常见,就是项目中规定,不能把与数据库表映射的entity类返回给前端,所以通常返回给前端的有各种O,比如:XxxVO、XxxBO、XxxDTO...

这时候就会出现下面的场景,大家也想已经猜到了。

下面是与数据库表映射的UserEntity

Nous pouvons encapsuler certains getters et setters dans une méthode d'usine, puis pour l'utilisateur, il suffit d'appeler la méthode, pas besoin de savoir comment les getters et les setters à l'intérieur sont gérés. Nous pouvons également utiliser Implémentation fournie par JDKCloneable pour obtenir une copie rapide.

Quatre façons de créer des objets :

nouveau, réflexion, Clone, sérialiser🎜

Cas réel

🎜Quelqu'un a-t-il rencontré ? problème courant. Il est stipulé dans le projet que la classe d'entité mappée avec la table de base de données ne peut pas être renvoyée au front-end, il y a donc généralement divers O renvoyés au front-end, tels que : XxxVO, XxxBO, XxxDTO...🎜 🎜À ce moment-là, la scène suivante apparaîtra, et tout le monde l'aura peut-être deviné. 🎜🎜Ce qui suit est le mappage avec la table de la base de donnéesUserEntityClasse d'entité. 🎜<pre class="brush:php;toolbar:false;">public class UserEntity { private Long id; private String name; private Integer age; //....可能还有很多属性 //省略getter setter }</pre>🎜Classe d'entité UserVO renvoyée au front-end ou à l'appelant. 🎜<pre class="brush:php;toolbar:false;">public class UserVO { private Long id; private String name; private Integer age; //....可能还有很多属性 //省略getter setter }</pre>🎜À ce stade, la UserEntity trouvée dans la base de données doit être convertie en UserVO puis renvoyée au front-end (ou à l'appelant). 🎜<pre class="brush:php;toolbar:false;">public class ObjectConvertUtil { public static UserVo convertUserEntityToUserVO(UserEntity userEntity) { if (userEntity == null) { return null; } UserVo userVo = new UserVo(); userVo.setId(userEntity.getId()); userVo.setName(userEntity.getName()); userVo.setAge(userEntity.getAge()); //如果还有更多属性呢? return userVo; } }</pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">从这个util类中,我们可以看出,如果一个类的属性有几十个,上百个的,这代码量是不是有点恐怖?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">于是,我们通常都会使用一些工具类来处理,比如常见有以下:</p><pre class="brush:php;toolbar:false;">BeanUtils.copy(); JSON.parseObject() Guava工具类 .....</pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">这些工具类就用到了原型模式。</p> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: rgb(255, 249, 249);"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">通过一个对象,创建一个新的对象。</p></blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">也把原型模式称之为对象的拷贝、克隆。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">其实对象的克隆分浅克隆和深克隆,下面我们就来聊聊浅克隆和深克隆。</p> <ul class="list-paddingleft-2" data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;"> <li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。</section></li> <li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。</section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">我们先来聊聊浅克隆,都喜欢由浅入深。</p> <h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"> <span style="display: none;"></span><span style="display: inline-block;background: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">浅克隆</span><span style="display: inline-block;vertical-align: bottom;border-bottom: 36px solid #efebe9;border-right: 20px solid transparent;"> </span> </h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;margin-top: 1px;margin-bottom: 1px;">比如,我现在相对用户信息User进行克隆,但是User中有用户地址信息<code style='font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);'>UserAddress属性。

以下是代码的实现:

//用户地址信息
public class UserAddress  implements Serializable{
    private String province;
    private String cityCode;

    public UserAddress(String province, String cityCode) {
        this.province = province;
        this.cityCode = cityCode;
    }
}
//用户信息
public class User implements Cloneable {
    private int age;
    private String name;
    //用户地址信息
    private UserAddress userAddress;

    //getter setter 省略

    @Override
    protected Object clone() throws CloneNotSupportedException { 
        return super.clone();
    }
}
//测试
public class UserTest {
    public static void main(String[] args) throws Exception {
        User user = new User();
        user.setAge(20);
        user.setName("田维常");
        UserAddress userAddress = new UserAddress("贵州", "梵净山");
        user.setUserAddress(userAddress);

        User clone = (User) user.clone();

        System.out.println("克隆前后UserAddress比较:" + (user.getUserAddress() == clone.getUserAddress()));
    }
}

输出结果

克隆前后 UserAddress 比较:true

两个对象属性 UserAddress 指向的是同一个地址。

这就是所谓的浅克隆,只是克隆了对象,对于该对象的非基本类型属性,仍指向原来对象的属性所指向的对象的内存地址。

关系如下:

Mode prototype maître en cinq minutes


深克隆

关于深克隆,我们来用一个很经典的案例,西游记里的孙悟空。一个孙悟空能变成n多个孙悟空,手里都会拿着一个金箍棒。

按照前面的浅克隆,结果就是:孙悟空倒是变成很多孙悟空,但是金箍棒用的是同一根。

深克隆的结果是:孙悟空变成了很多个,金箍棒也变成很多个根。

下面我们用代码来实现:

//猴子,有身高体重和生日
public class Monkey {
    public int height;
    public int weight;
    public Date birthday;
}

孙悟空也是猴子,兵器 孙悟空有个金箍棒:

import java.io.Serializable;
//孙悟空的金箍棒
public class JinGuBang implements Serializable{
    public float  h=100;
    public float  d=10;
    //金箍棒变大
    public void big(){
        this.h *=10;
        this.d *=10;
    }
    //金箍棒变小
    public void small(){
        this.h /=10;
        this.d /=10;
    }
}

齐天大圣孙悟空:

import java.io.*;
import java.util.Date;

//孙悟空有七十二变,拔猴毛生成一个金箍棒
//使用JDK的克隆机制,
//实现Cloneable并重写clone方法
public class QiTianDaSheng extends Monkey implements Cloneable, Serializable {

    public JinGuBang jinGuBang;

    public QiTianDaSheng() {
        this.birthday = new Date();
        this.jinGuBang = new JinGuBang();
    }

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

    //深克隆
    public QiTianDaSheng deepClone() {
        try {
            //内存中操作完成、对象读写,是通过字节码直接操作
            //与序列化操作类似
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream bis = new ObjectInputStream(bais);

            //完成一个新的对象,底层是使用new创建的一个对象
            //详情可以了解readObject方法
            QiTianDaSheng qiTianDaSheng = (QiTianDaSheng) bis.readObject();
            //每个猴子的生日不一样,所以每次拷贝的时候,把生日改一下
            qiTianDaSheng.birthday = new Date();
            return qiTianDaSheng;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    //浅克隆,就是简单的赋值
    public QiTianDaSheng shalllowClone(QiTianDaSheng target) {
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        qiTianDaSheng.height = target.height;
        qiTianDaSheng.weight = target.weight;

        qiTianDaSheng.jinGuBang = target.jinGuBang;
        qiTianDaSheng.birthday = new Date();
        return qiTianDaSheng;

    }
}

接着我们就来测试一下:

public class DeepCloneTest {
    public static void main(String[] args) {
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
        try {
            QiTianDaSheng newObject = (QiTianDaSheng) qiTianDaSheng.clone();
            System.out.print("深克隆后 ");
            System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang));
            
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        
        QiTianDaSheng newObject=qiTianDaSheng.shalllowClone(qiTianDaSheng);
        System.out.print("浅克隆后 ");
        System.out.println("金箍棒是否一直:" + (qiTianDaSheng.jinGuBang == newObject.jinGuBang));
    }
}

输出结果为:

深克隆后 金箍棒是否一直:false
浅克隆后 金箍棒是否一直:true

结论

深克隆后每个孙悟空都有自己的金箍棒,而浅克隆后每个孙悟空用的金箍棒实质上还是同一根。

Mode prototype maître en cinq minutes

总结

切记:深和浅,指的是克隆对象里的属性(引用类型)是否指向同一个内存地址。

为了更深刻的理解深克隆和浅克隆,我们回答文中的简历拷贝的故事。

  • Copie approfondie : copiez un CV, puis modifiez les informations du CV pour le personnaliser
  • Copie superficielle : copiez un CV et le contenu du CV reste complètement inchangé

Avantages :

  • Le mode prototype Java est basé sur la copie de flux binaire en mémoire, qui offre de meilleures performances que le nouveau direct.
  • Vous pouvez utiliser le clonage profond pour enregistrer l'état de l'objet, enregistrer une ancienne copie (la cloner) et lors de sa modification, il peut agir comme une fonction d'annulation.

Inconvénients :

  • nécessite de configurer la méthode de clonage. Lors de la transformation, les classes existantes doivent être modifiées, ce qui viole le "principe d'ouverture et de fermeture".
  • S'il existe plusieurs références imbriquées entre les objets, chaque calque doit être cloné.

Nous avons donné une explication complète du modèle de prototype à partir de sa définition, des scénarios d'utilisation, des cas réels, du clonage superficiel, du clonage profond, des avantages et des inconvénients, etc.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer