设计模式—原型模式(Prototype)

news2024/10/5 16:20:19

目录

一、什么是原型模式?

二、原型模式具有什么优缺点吗?

三、有什么缺点?

四、什么时候用原型模式?

五、代码展示

①、简历代码初步实现

②、原型模式

③、简历的原型实现

④、深复制

⑤、浅复制

一、什么是原型模式?

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节

简单说就是先创建一个原型类实例,然后通过克隆的方法来复制一个一样的新对象,这个对象和原来的对象相同或相似

核心:通过自带的Clone()方法和ICloneable接口创建跟该对象相同的新对象,不需要知道具体的创建细节。

二、原型模式具有什么优缺点吗?

  • 隐藏对象创建细节,提高性能:一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。
  • 不用重新初始化对象,而是动态地获得对象运行时的状态;

三、有什么缺点?

  • 根据实际应用需要涉及到深浅复制问题;
  • 每一个类都需要一个Clone方法;
  • Clone方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背开闭原则;

四、什么时候用原型模式?

  • 同一个类当需要实例化多次的时候;
  • 对象之间相同或相似,即只是个别的几个属性不同的时候;


五、代码展示

场景:小菜最近在找工作,需要复印大量的简历发给求职公司,小菜和大鸟通过复制简历这个事情来谈到了了六七年前和现在复印简历的不同:六七年前:简历一般都是手写,很珍贵;现在:只要有一份简历原版,直接复印就可以了。对于编程来说,直接Ctrl+C和Ctrl+V直接复制代码就可以了,但简单的复制代码有时候也会带来重复代码的灾难的。大鸟要求小菜写一个简历类,里面必须有姓名、性别、年龄、工作经历,最后要求有三份简历。

①、简历代码初步实现

简历类

//简历
class Resume
{
        private string name;         //姓名
        private string sex;          //性别
        private string age;         //年龄
        private string timeArea;        //工作时间
        private string company;         //公司

        public Resume(string name)      //有参的构造方法
        {
            this.name = name;           //赋值
        }

        //设置个人信息
        public void SetPersonalInfo(string sex,string age)
        {
            this.sex = sex;   //赋值
            this.age = age;     //赋值
        }

        //设置工作经历
        public void SetWorkExperience(string timeArea,string company)
        {
            this.timeArea = timeArea;       //赋值
            this.company = company;         //赋值
        }

        //显示
        public void Display()
        {
            Console.WriteLine("{0}{1}{2}", name, sex, age);   //姓名,性别,年龄
            Console.WriteLine("工作经历:{0}{1}", timeArea, company); //工作经历:工作时间,公司
        }
}

客户端调用代码

class Program
{
        static void Main(string[] args)
        {
            #region 第一种方法
            //Resume a = new Resume("大鸟 ");              //实例化简历对象
            //a.SetPersonalInfo("男", "29");               //设置个人信息
            //a.SetWorkExperience("1998-2000 ", "xx公司");   //设置工作经历

            //Resume b = new Resume("大鸟 ");
            //b.SetPersonalInfo("男 ", "29");
            //b.SetWorkExperience("1998-2000 ", "xx公司");

            //Resume c = new Resume("大鸟 ");
            //c.SetPersonalInfo("男 ", "29 ");
            //c.SetWorkExperience("1998-2000 ", "xx公司");                        
            #endregion
                
            缺点:每new一次,都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行这个初始化操作就很低效。
                  三份简历就需要实例化3次,如果是需要20份简历,就需要实例化20次,如果98写成99年,就需要改20次。
                  每次实例化出来的对象都存放在堆内存里面,按照这样的方式实例化对堆内存的消耗就非常大。
            如何改进这个问题呢?可以使用第二种方法,先实例化出来一个对象a,然后把a引用所指向的地址赋值给b和c,这样在堆内存里面
            
            #region 第二种方法
            Resume a = new Resume("大鸟 ");              //实例化简历对象
            a.SetPersonalInfo("男", "29");               //设置个人信息
            a.SetWorkExperience("1998-2000 ", "xx公司");   //设置工作经历

            Resume b = a;
            Resume c = a;
            #endregion

            a.Display();                //显示
            b.Display();
            c.Display();

            Console.ReadKey();
        }
}

如何解决?Clone克隆

②、原型模式

Prototype类

abstract class Prototype           //抽象原型类
{
        private string id;          //私有的成员变量
        public Prototype(string id) //有参的构造方法
        {
            this.id = id;
        }

        public string Id           //属性
        {
            get { return id; }     //取值
        }

        public abstract Prototype Clone();   //抽象克隆方法
}

ConcretePrototype1

class ConcretePrototype1 : Prototype      //继承原型类
{
       private string id;
        public ConcretePrototype1(string id) : base(id) { }  //重写有参的构造方法
        
        public override Prototype Clone()   //重写原型类抽象方法
        {
            //创建当前对象的浅表副本
            return (Prototype)this.MemberwiseClone();    //返回Prototype类型的克隆对象,括号里的作用是指定要创建的对象种类
        }
} 

③、简历的原型实现

思想

Resume:

  • 记录相关属性、方法、变量
  • Clone()方法

客户端

  1. 实例化一个原型对象
  2. 调用要复制类的Clone()方法进行复制,并返回复制完的对象

方法:创建一个新对象,然后将当前对象的非静态字段复制到该新对象

  (复制类型)this.MemberwiseClone();

  值类型:逐位复制

  引用类型:复制引用但不赋值引用的对象

  1. 可修改简历
  2. 显示

简历类

class Resume:ICloneable
{
        private string name;         //姓名
        private string sex;          //性别
        private string age;          //年龄
        private string timeArea;        //工作时间
        private string company;         //公司

        public Resume(string name)      //有参的构造方法
        {
            this.name = name;           //赋值
        }

        //设置个人信息
        public void SetPersonalInfo(string sex, string age)
        {
            this.sex = sex;   //赋值
            this.age = age;     //赋值
        }

        //设置工作经历
        public void SetWorkExperience(string timeArea, string company)
        {
            this.timeArea = timeArea;       //赋值
            this.company = company;         //赋值
        }

        //显示
        public void Display()               //实现接口的方法,用来克隆对象
        {
            Console.WriteLine("{0}{1}{2}", name, sex, age);   //姓名,性别,年龄
            Console.WriteLine("工作经历:{0}{1}", timeArea, company); //工作经历:工作时间,公司
        }

        public Object  Clone()      //克隆的方法
        {
            return (Object)this.MemberwiseClone();
        }
}

客户端

static void Main(string[] args)
{
            Resume a = new Resume("大鸟");       //实例化简历类,调用Resume类有参的构造方法
            a.SetPersonalInfo("男", "29");       //调用SetPersonalInfo方法,并传值
            a.SetWorkExperience("1998-2000", "xx公司"); //调用SetWorkExperience方法,并传值

            //调用Clone方法,实现新简历的生成,并且可以再修改新简历的细节
            Resume b = (Resume)a.Clone();         //克隆a对象,并赋值给简历对象b,此时a和b一样
            b.SetWorkExperience("1998-2006", "yy公司");       //b对象可以修改自己简历的细节

            Resume c = (Resume)a.Clone();          //克隆a对象给简历对象c
            c.SetPersonalInfo("男", "24");         //c对象可以修改自己简历的细节

            a.Display();       //显示简历a
            b.Display();       //显示简历a
            c.Display();       //显示简历a

            Console.ReadKey();  //等待用户按下某键退出
}

问题:‘简历’对象里的数据都是string类型的,如果类当中有对象引用,那么引用的对象数据是不会被克隆过来的。

④、深复制

WorkExperience类

//工作经历类
class WorkExperience : ICloneable
{
        private string workDate;
        public string WorkDate
        {
            get { return workDate; }
            set { workDate = value; }
        }

        private string company;
        public string Company
        {
            get { return company; }
            set { company = value; }
        }

        public Object Clone()       //让“工作经历”类实现克隆方法
        {
            return (Object)this.MemberwiseClone();
        }
}

Resume类

// 简历类
class Resume:ICloneable
{
        private string name;
        private string age;
        private string sex;
        private WorkExperience work;

        public Resume (string name)     //有参的构造方法
        {
            this.name = name;           //赋值姓名
            work = new WorkExperience();  //在“简历”类实例化时同时实例化“工作经历”
        }

        //提供Clone方法调用的私有构造函数,以便克隆“工作经历”的数据
        private Resume(WorkExperience work)
        {
            this.work = (WorkExperience)work.Clone();
        }
        public  void SetPersonalInfo(string sex,string age)   //设置个人信息
        {
            this.sex = sex;   //赋值性别
            this.age = age;   //赋值年龄
        }

        public void SetWorkExperience (string workDate,string company)  //设置工作经历
        {
            work.WorkDate = workDate;   //赋值工作时间
            work.Company = company;     //赋值公司
        }

        public void Display()           //显示
        {
            Console.WriteLine("{0} {1} {2}", name, sex, age);
            Console.WriteLine("工作经历:{0}{1}", work.WorkDate, work.Company);
        }
        public Object Clone()         //克隆方法
        {
            //调用私有的构造方法,让“工作经历”克隆完成,
            //然后再给这个“简历”对象的相关字段赋值,
            //最终返回一个深复制的简历对象
            Resume obj = new Resume(this.work);
            obj.name = this.name;
            obj.sex = this.sex;
            obj.age = this.age;
            return obj;
        }
}

⑤、浅复制

WorkExperience类

//工作经历
class WorkExperience
{
        private string workDate;    //字段:时间区间
        public string WorkDate     //属性:时间区间
        {
            get { return workDate; }        //读
            set { workDate = value; }       //写
        }

        private string company;    //字段:公司
        public string Company     //属性:公司
        {
            get { return company; }       //读
            set { company = value; }      //写
        }
}

Resume类

class Resume
{
        private string name;     //姓名
        private string sex;      //性别
        private string age;      //年龄        
        private WorkExperience work;    //工作经历

        public Resume(string name)   //构造方法
        {
            this.name = name;    //赋值姓名
            work = new WorkExperience(); //在“简历”类实例化时同时实例化“工作经历”
        }

        public void SetPersonalInfo(string sex, string age) //设置个人信息
        {
            this.sex = sex; //赋值性别
            this.age = age; //赋值年龄
        }

        public void SetWorkExperience(string workDate, string company) //设置工作经历
        {
            work.WorkDate=workDate; //赋值工作时间
            work.Company=company;    //赋值公司
        }

        public void Display()           //显示
        {
            Console.WriteLine("{0} {1} {2}", name, sex, age);         //输出姓名,性别,年龄
            Console.WriteLine("工作经历:{0}{1}", work.WorkDate, work.Company); //输出工作经历:工作时间,公司
        }

        public Object Clone()       //克隆
        {
            return (Object)this.MemberwiseClone();  //返回Object类型的克隆对象
        }
}

客户端程序代码

Resume a = new Resume("大鸟");    //实例化一个叫大鸟的简历对象,调用有参构造方法
a.SetPersonalInfo("男", "29");    //调用方法,赋值性别,年龄
a.SetWorkExperience("1998-2000", "xx公司");   //调用方法,赋值工作时间,公司

Resume b = (Resume)a.Clone();       //克隆对象a给b
b.SetWorkExperience("1998-2006", "yy公司"); //修改细节
//b和c都克隆于a,但当它们都设置了“工作经历”时,我们希望的结果是三个的显示不一样

Resume c = (Resume)a.Clone();        //克隆对象a 给c
c.SetPersonalInfo("男", "24");       //调用方法,赋值性别,年龄
c.SetWorkExperience("1998-2003", "zz公司");   //修改细节
                
a.Display();        //显示简历a
b.Display();        //显示简历b   
c.Display();        //显示简历c

Console.ReadKey();

它在内存里面的形式是这样的。


六、涉及到的知识点

1、一个类里面有哪些东西?

2、类和实例

什么是类?

就是具有相同的属性和功能的对象的抽象的集合。注意:

  • 类名称首字母大写。多个单词则各个首字母大写;
  • 对外公开的方法需要用‘public’修饰符。

什么是实例?

就是一个真实的对象。比如我们都是‘人’,而你和我其实就是‘人’类的实例了。

什么是实例化?

创建对象的过程,使用new关键字来创建。

Cat cat = new Cat();     //其实做了两件事情

Cat cat;          //第一步、声明一个Cat的对象,对象名为cat
cat = new Cat();  //第二步、将此cat对象实例化

3、字段和属性

什么是字段?

是存储类要满足其设计所需要的数据,字段是与类相关的变量。

private  string  name = "";     //name就是一个字段,私有的类变量

注意

  • 如果在定义字段时,在字段的类型前面使用了readonly关键字,那么字段就为只读字段,它只能在以下两个位置被赋值或者传递到方法中被改变。
  • 在定义字段时赋值;
  • 在类的构造函数内被赋值,或传递到方法中被改变,而且在构造函数中可以被多次赋值。

属性是什么?

是一个方法或一对方法体。提供对类或对象的访问。

属性怎么用呢?

它有两个方法get和set。

get访问器:从属性获取值。返回与声明的属性相同的数据类型,表示的意思是调用时可以得到内部字段的值或引用;

set访问器:为属性赋值。没有显式设置参数,但它有一个隐式参数,用关键字value表示,它的作用是调用属性时可以给内部的字段或引用赋值。

属性有什么作用?

限制外部类对类中成员的访问权限,定义在类级别上。

private int _age;          //年龄
public int Age                    
{
    get                    //也可以直接在属性中进行判断操作、设置限制
    {
        if (_age >= 0 && _age <= 150)     //如果年龄大于 0并且小于150的,(表示输入正确)
        {
        	return _age;                  //则返回输入的年龄
        }
        else                              //否则,(表示输入错误)
        {
         	return 18;                    //返回指定年龄18
        }
     }

    set { _age = value; }
}

静态属性是什么?

在属性前面加static关键字,这个属性就成为了静态属性。

有什么作用呢?

  • 不管类是否有实例,它们都是存在的。
  • 当从类的外部访问时,必须使用类名引用,而不是实例名。
class Person
{
    private static  string name;   //字段
    public static string Name      //属性
    {
        get { return name; }
        set { name = value; }
    }
}
static void Main(string[] args)
{
	Person.Name = "小菜";      //不需要实例化Person类即可直接对属性赋值
}

属性和字段的公有部分解释:

内存:

  • 字段:分配内存
  • 属性:不分配内存

命名规范:

  • 字段:Camel大小写
  • 属性:Pascal小写

4、修饰符

在变量前面可以加上访问修饰符(readonly、static)

readonly(只读):读取该字段的值不能给字段赋值

static:静态字段,可以直接通过类名访问该字段

5、访问修饰符有哪些?

  • public:公有的,对任何类可访问
  • private:私有的,只在类的内部可以访问,成员默认是这个
  • protected:保护的,只允许该类的派生类访问
  • internal:内部的,同一项目所有类可访问                

6、this关键字传递的是什么?

  • 用于区分类的成员和本地变量或参数;
  • 作为调用方法的实参                
//this调用成员变量或成员方法
class Person
{
    private string name;               //字段

    public void setName(string name)    //方法
    {
    	this.name = name;       //将参数值赋予类中的成员变量
    }
    //成员变量和setName()方法中的形式参数的名称相同,都为name,那么如何区分使用的是哪一个变量呢?
    //使用this关键字代表本类对象的引用,this.name指Person类中name成员变量,等号后面的name指传过来的形参name
}
this作为方法的返回值
public Book getBook()
{
    return this;                 //返回Book类引用
}

在getBook()类中,方法的返回值为Book类,所以方法体中使用return this这种形式将Book类的对象返回

this关键字和对象之间有什么关系?

this引用的就是本类的一个对象。

如果省略this会怎么样?

直接写成name=name,只是把形参name赋值给参数变量本身而已,成员变量name的值没有改变            

7、构造方法

  • 什么时候用?就是对类进行初始化(在创建该类的对象时就会调用)。
  • 有哪些特点?与类同名

     无返回值

     不需要void,在new时候调用      

//希望每个小猫一诞生就有姓名
class Cat
{
    private string name ="";        //声明Cat类的私有字符串变量name
    public Cat(string name)         //定义Cat类的构造方法,参数是输入一个字符串
    {
        this.name =name;            //将参数赋值给私有变量name
    }
    
    public string Shout()
    {
        return "我的名字叫"+name+" 喵";
    }
}

注:所有类都有构造方法,如果你不编码则系统默认生成空的构造方法,若有定义的构造方法,那么默认的构造方法就会失效(这个构造方法什么也不做,只是为了让用户能够顺利地实例化)

8、方法重载

是什么?指方法名相同,但参数的数据类型、个数或顺序不同的方法。(一同二不同)

有什么好处?在不改变原方法的基础上,新增功能。

class Animal
{
    private string name;
    
    //方法重载:方法名相同、数据类型、个数/顺序不同
    public Animal(){}          //无参的构造方法
    public Animal(string name) //有参的构造方法
    {
        this.name = name;
    }
}

9、抽象类

什么是抽象类?

目的:抽取相同代码,实现封装思想

特点:

  • 抽象类不能实例化;
  • 抽象方法是必须被子类重写的方法;
  • 如果类中包含抽象方法,那么类就必须定义为抽象类,不论是否还包含其他一般方法

什么是重写?

将父类实现替换为它自己的实现

虚成员

抽象成员

关键字

virtual

abstract

实现体

有实现体

没有实现体,被分号取代

在派生类中被覆写

可重写,也可不重写,使用override

必须被重写,使用override

10、值类型和引用类型

值类型:修改其中一个对另一个不会有影响,在栈上存的是值

引用类型:对第二个变量修改会影响到第一个变量。因为他们指向同一个引用。在栈上存的是地址,堆上存的是值

11、六大原则

六大原则

12、六大关系

六大关系


七、思维导图

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

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

相关文章

2023-8-28 树的重心(树与图的深度优先遍历)

题目链接&#xff1a;树的重心 #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N 100010, M N * 2;int n; int h[N], e[M], ne[M], idx; int ans N; bool st[N];void add(int a, int b) {e[idx] b, ne[id…

小鹏接手MONA,滴滴造了一台什么样的车?

作者|张祥威 编辑|德新 8月28日&#xff0c;小鹏汽车宣布和滴滴出行达成战略合作。 除了资本层面的合作&#xff0c;双方合作的业务核心是代号「MONA」的车型项目。 根据协议&#xff1a; 双方将打造一款售价15万元级别的A级纯电动轿车&#xff0c;项目代号MONA&#xff1b…

5.Redis-string

string 字符串 字符串类型是 Redis 最基础的数据类型&#xff0c;关于字符串需要特别注意&#xff1a; 1.⾸先Redis中所有 key 的类型都是字符串类型&#xff0c;⽽且其他⼏种数据结构也都是在字符串类似基础上构建的&#xff0c;例如 list 和 set 的元素类型是字符串类型。 2…

遥遥领先?实际是落后两代以上,小丑挡不住更多消费者买iPhone

谈到国产手机的时候&#xff0c;谁都知道一个品牌&#xff0c;都说遥遥领先苹果&#xff0c;然而事实上呢所有安卓手机都落后苹果两代&#xff0c;正是这样的现实让国内消费者越来越多买iPhone&#xff0c;推动苹果在中国高端手机市场逼近八成份额。 1.苹果为何领先&#xff1f…

Android12之ABuffer数据处理(三十五)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

WebGL:开始学习 / 理解 WebGL / WebGL 需要掌握哪些知识 / 应用领域 / 前端值得学WebGL吗

一、WebGL发展史 2006 年&#xff0c;WebGL 的发展史可以追溯到 2006 年左右&#xff0c;当时 Mozilla Foundation 的一个开发人员 Vladimir Vukićević 开始尝试在 Firefox 浏览器中嵌入 OpenGL&#xff0c;为 JavaScript 提供底层图形库的支持。随后&#xff0c;这个项目引…

IDEA插件推荐 - Grep Console - 控制台日志过滤的插件

装上该插件之后&#xff0c;我们就可以很方便的进行日志的过滤、筛选。

周鸿祎为360智脑招贤纳士;LLM时代的选择指南;Kaggle大语言模型实战;一文带你逛遍LLM全世界 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 思否「齐聚码力」黑客马拉松&#xff0c;用技术代码让生活变得更美好 主页&#xff1a;https://pages.segmentfault.com/google-hacka…

智通三千亮相2023中国软博会

“软件赋智&#xff0c;数实融合。”8月20日—23日&#xff0c;2023中国国际软件产品和信息服务交易博览会在南京举办。软博会连续举办19届。期间&#xff0c;《2023年我国工业软件产业发展研究报告》《中国软件产业高质量发展紫金指数&#xff08;2023&#xff09;》等发布。 …

c++查漏补缺(1)

目录 1.explicit关键字 2.static关键字 3.友元函数 1.explicit关键字 exeplicit关键字是在构造函数要使用的关键字。可以防止“隐式构造”&#xff0c;例如&#xff1a; #include<iostream>using namespace std;class Date { public:explicit Date(int year, int mo…

【科研论文配图绘制】task5 SciencePlots绘图包入门

【科研论文配图绘制】task5 SciencePlots绘图包入门 task5主要学习了SciencePlots拓展包的出图样式&#xff0c;掌握SciencePlots的安装及具体使用。 SciencePlots作为一个专门用于科研论文绘图的第三方拓展工具包&#xff0c;提供了主流英文科技 期刊(如 Nature、Science 和 …

1、监测数据采集物联网应用开发步骤(1)

项目介绍 本文章编写目的针对下图中《。。。解决方案》所涉及的开发资料&#xff1b; 监测数据采集物联网应用解决方案_yong427的博客-CSDN博客 开发步骤实现从0开始搭建软件框架&#xff0c;该开发步骤基于python3.0语言及相关工具实现&#xff0c;阅读本文章之前请先初步百…

MyBatis学习简要

目录 什么是MyBatis? MyBatis实现的设想 MyBatis基于配置文件的开发步骤 mybatis的配置文件 Mapper代理开发 配置文件完成增删改查的三步 注解开发 一、条件查询 参数接收时&#xff0c;参数的设置&#xff1a; 动态条件查询&#xff1a; 二、添加功能 步骤&#xf…

Eclipse打jar包与JavaDOC文档的生成

补充知识点——Eclipse打jar包与JavaDOC文档的生成 1、Eclipse如何打jar包&#xff0c;如何运行jar包 Java当中编写的Java代码&#xff0c;Java类、方法、接口这些东西就是项目中相关内容&#xff0c;到时候我们需要把代码提供给甲方、或者是我们需要运行我们编写的代码&…

《2023年网信人才培训-网络安全从业人员能力素养提升培训》第一期成功举办

随着网络强国和数字中国建设的步伐加快&#xff0c;建设规模宏大、结构合理、素质优良的人才队伍成为一项重要工作。知了汇智作为数字产教融合基地&#xff0c;通过与高校、企业等多方合作&#xff0c;建立了完整的网络安全人才培养生态链。凭借自身技术优势和丰富的产业资源&a…

【Go 基础篇】探索Go语言中的Map:数据的魔法盒子

嗨&#xff0c;各位Go语言的探索者们&#xff01;在我们的编程世界中&#xff0c;总会有一些特殊的工具能够让我们的生活变得更加便捷。而在Go语言中&#xff0c;Map就是这样一种神奇的数据结构。它就像是一个魔法盒子&#xff0c;可以帮助我们高效地存储和操作键值对数据。本文…

python技术栈之单元测试中mock的使用

什么是mock&#xff1f; mock测试就是在测试过程中&#xff0c;对于某些不容易构造或者不容易获取的对象&#xff0c;用一个虚拟的对象来创建以便测试的测试方法。 mock的作用 特别是开发过程中上下游未完成的工序导致当前无法测试&#xff0c;需要虚拟某些特定对象以便测试…

Spring(九)声明式事务

Spring整合Junit4和JdbcTemplater如下所示&#xff1a; 我们所使用的junit的jar包不同&#xff0c;可以整合不同版本的junit。 我们导入的依赖如下所示&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.a…

SAP_ABAP_DEBUG_场景介绍

SAP ABAP顾问能力模型_企业数字化建设者的博客-CSDN博客SAP Abap顾问能力模型https://blog.csdn.net/java_zhong1990/article/details/132469977一 背景说明 二 场景说明 1 SAPerp与第三方系统集成&#xff0c;第三系统通过接口函数请求SAP系统&#xff0c;如何调试&#xff1…

加密的PDF文件,如何解密?

PDF文件带有打开密码、限制编辑&#xff0c;这两种密码设置了之后如何解密&#xff1f; 不管是打开密码或者是限制编辑&#xff0c;在知道密码的情况下&#xff0c;解密PDF密码&#xff0c;我们只需要在PDF编辑器中打开文件 – 属性 – 安全&#xff0c;将权限状态修改为无保护…