Skip to content

设计模式(一)

约 8742 字大约 29 分钟

2017-05-19

创建型设计模式。

概述

设计模式是在软件设计中经常出现的问题的通用解决方案。这些解决方案是经过反复验证和证明的,并且被广泛接受和应用于软件开发中。设计模式提供了一种可重用的思想框架,可以帮助开发人员更有效地解决常见问题,并提高代码的可维护性、可读性和可扩展性。

编程是一门技术,更加是一门艺术,不能只满足于写完代码运行结果正确就完事,时常考虑如何让代码更加简练,更加容易维护,容易扩展和复用,只有这样才可以真正得到提高。

设计原则

设计模式的使用应该遵循一些基本的设计原则,这些原则有助于编写更清晰、灵活、可维护和可扩展的代码。以下是一些常见的设计原则:

  1. 单一职责原则(Single Responsibility Principle - SRP): 一个类应该只有一个修改的理由。每个类都应该专注于一项任务,避免一个类担负过多的职责。

  2. 开闭原则(Open-Closed Principle - OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。可以通过接口和抽象类实现。

  3. 里氏替换原则(Liskov Substitution Principle - LSP): 派生类应该能够替换其基类而不影响程序的正确性。即,子类应该能够替代父类并保持程序的行为一致性。

  4. 依赖倒置原则(Dependency Inversion Principle - DIP): 高层模块不应该依赖于底层模块,二者都应该依赖于抽象。抽象不应该依赖于细节,而细节应该依赖于抽象。

  5. 接口隔离原则(Interface Segregation Principle - ISP): 客户端不应该强制依赖于它们不使用的接口。一个类不应该被强制实现它用不到的接口。

  6. 迪米特法则(Law of Demeter,LoD,又称最少知识原则): 一个对象应该对其他对象有最少的了解。避免在一个类中调用过多其他类的方法,减少耦合性。

  7. 合成复用原则(Composition/Aggregation Reuse Principle - CARP): 首选使用组合/聚合而不是继承来实现复用。通过组合可以更灵活地实现对象间的关系。

  8. 优先使用对象组合,而不是类继承(Favor Composition Over Inheritance): 对象组合通常比类继承更加灵活,能够减少耦合性并支持代码重用。

这些原则通常被认为是良好面向对象设计的基石,它们相互交织,协同工作,帮助设计出符合软件工程最佳实践的系统结构。在实践中,根据具体情况灵活运用这些原则,有助于构建更健壮和可维护的软件系统。

UML

UML(Unified Modeling Language)类图是一种用于描述系统中的类、对象及它们之间关系的图形化建模工具。它是一种强大的面向对象的建模语言,被广泛用于软件开发和系统设计。以下是类图的主要元素和介绍:

  1. 类(Class):类是对现实世界中一组相似对象的抽象,它定义了对象的属性和行为。在类图中,类通常用一个矩形框表示,矩形框分为三个部分:类名、属性(字段)和方法。

  2. 关联(Association): 关联表示类之间的关系,描述一个类的实例和另一个类的实例之间的连接。关联可以是单向的或双向的,可以具有方向性,也可以是多重性的,表示一个类中的实例与另一个类中的实例之间的关系。

  3. 聚合(Aggregation): 表示一种弱的拥有关系,整体对象可以包含部分对象,但部分对象并不是整体对象的一部分。聚合用一条带有空心菱形的直线表示。

  4. 组合(Composition): 表示一种强的拥有关系,整体对象包含部分对象,部分对象是整体对象的一部分。组合用一条带有实心菱形的直线表示。

  5. 泛化(Generalization): 表示类之间的继承关系,其中一个类是另一个类的子类。泛化用一条带有空心三角箭头的直线表示。

  6. 接口(Interface): 表示一个类或一组类的契约,它规定了这些类应该实现的一组方法。接口用带有 interface 标记的小矩形表示。

  7. 依赖(Dependency): 表示一个类使用了另一个类的服务,但是并不拥有对方的对象。依赖关系用一条带有箭头的虚线表示。

  8. 多重性(Multiplicity): 表示关联、聚合或组合关系中每个端点的实例数目。例如,1..* 表示一个或多个实例。

UML类图是软件开发中非常有用的工具,它能够清晰地展示系统中的类、对象和它们之间的关系,帮助开发人员理解系统结构、设计和实现。通过类图,开发团队能够更好地沟通和协作,确保系统的正确性和可维护性。

先说结论

创建型模式

  1. 工厂方法模式(Factory Method Pattern) :定义一个创建对象的接口,但是让子类决定实例化哪个类。工厂方法使得一个类的实例化延迟到其子类。

  2. 抽象工厂模式(Abstract Factory Pattern) :提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

  3. 单例模式(Singleton Pattern) :确保一个类只有一个实例,并提供一个全局访问点。

  4. 建造者模式(Builder Pattern) :将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。

  5. 原型模式(Prototype Pattern) :用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

结构型模式

  1. 适配器模式(Adapter Pattern) :将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。

  2. 桥接模式(Bridge Pattern) :将抽象部分与它的实现部分分离,使它们可以独立变化。

  3. 组合模式(Composite Pattern) :将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

  4. 装饰器模式(Decorator Pattern) :动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。

  5. 外观模式(Facade Pattern) :为子系统中的一组接口提供一个统一的接口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

  6. 享元模式(Flyweight Pattern) :运用共享技术有效地支持大量细粒度的对象。

  7. 代理模式(Proxy Pattern) :为其他对象提供一种代理以控制对这个对象的访问。

行为型模式

  1. 责任链模式(Chain of Responsibility Pattern):为请求创建一个接收者对象的链,使得多个对象都有机会处理该请求。在请求发送者和接收者之间解耦。

  2. 命令模式(Command Pattern) :将一个请求封装成一个对象,从而使用户可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

  3. 解释器模式(Interpreter Pattern) :给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

  4. 迭代器模式(Iterator Pattern):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

  5. 中介者模式(Mediator Pattern) :用一个中介对象封装一系列对象的交互,中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

  6. 备忘录模式(Memento Pattern) :在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

  7. 观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。

  8. 状态模式(State Pattern):允许对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

  9. 策略模式(Strategy Pattern) :定义一系列的算法,把它们封装起来,并且使它们可以相互替换。策略模式使得算法可以独立于使用它的客户而变化。

  10. 模板方法模式(Template Method Pattern) :定义一个操作中的算法的框架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

  11. 访问者模式(Visitor Pattern) :表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

简单工厂模式

请用C++、Java、C#或VB.NET任意一种面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。

可以直接写,如下所示:

“且先不说出题人的意思,单就你现在的代码,就有很多不足的地方需要改进。”

  • A、B、C、D命名不规范;
  • 判断分支全用if,意味着每个条件都要判断;
  • 除数为0会产生错误;
  • 等等;

最重要一点是,没有体现出面向对象的意思。

考虑通过封装、继承、多态把程序的耦合度降低; 考虑用设计模式使得程序更加的灵活,容易修改,并且易于复用;

试着以面向对象的形式改写例子:

首先是一个运算类,它有两个Number属性,主要用于计算器的前后数,然后有一个虚方法GetResult(),用于得到结果,然后我把加减乘除都写成了运算类的子类,继承它后,重写了GetResult()方法,这样如果要修改任何一个算法,就不需要提供其他算法的代码了。但问题来了,我如何让计算器知道我是希望用哪一个算法呢

现在的问题其实就是如何去实例化对象的问题,教你一招‘简单工厂模式’,也就是说,到底要实例化谁,将来会不会增加实例化的对象,比如增加开根运算,这是很容易变化的地方,应该考虑用一个单独的类来做这个创造实例的过程,这就是工厂,来,我们看看这个类如何写。

只需要输入特定的符号,工厂就实例化出合适的对象,通过多态,返回父类的方式实现计算器的结果:

Q:如果有一天我们需要更改加法运算,我们只需要改哪里;

A:改OperationAdd就可以了。

Q:那么我们需要增加各种复杂运算,比如平方根,立方根,自然对数,正弦余弦等,如何做?

A:只要增加相应的运算子类,并且还需要去修改运算类工厂,在switch中增加分支。


工厂方法模式

再来看看工厂方法模式,与简单工厂模式还是有些差异。

简单工厂VS工厂方法

如果我现在需要增加其他运算,比如求M数的N次方,或者求M数的N次方根,这些功能的增加,在简单工厂里,我是先去加‘求M数的N次方’功能类,然后去更改工厂方法,当中加‘Case’语句来做判断,现在用了工厂方法,加功能类没问题,再加相关的工厂类,这也没问题,但要我再去更改客户端,这不等于不但没有减化难度,反而增加了很多类和方法,把复杂性增加了吗?为什么要这样?

这其实就是工厂方法模式和简单工厂的区别所在。简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。

但问题也就在这里,如你所说,如果要加一个‘求M数的N次方’的功能,我们是一定需要给运算工厂类的方法里加‘Case’的分支条件的,修改原有的类?这可不是好办法,这就等于说,我们不但对扩展开放了,对修改也开放了,违背了开闭原则

工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。你想要加功 能,本来是改工厂类的,而现在是修改客户端

工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点。


单例模式

通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

Singleton类,定义一个GetInstance操作,允许客户访问它的唯一实例。GetInstance是一个静态方法,主要负责创建自己的唯一实例。

多线程时的单例

多线程的程序中,多个线程同时,注意是同时访问Singleton类,调用GetInstance()方法,会有可能造成创建多个实例的。

可以给进程一把锁来处理。这里需要解释一下lock语句的涵义,lock是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

线程安全的单例实现方式有很多,这里记录四种常见的。

反序列化

虽然静态内部类方式实现单例模式通常被认为是线程安全的,但是有一种情况可能会导致它不安全,即在反序列化时。

在 Java 中,如果一个类可序列化(即实现了 Serializable 接口),那么它的实例可以被序列化为字节流,并在需要时反序列化成对象。但是,在反序列化过程中,会通过调用类的无参构造函数来创建对象,而不会调用类的任何其他构造函数。这就意味着,如果我们不小心让一个静态内部类单例模式的外部类可序列化,然后尝试对其进行反序列化,就会产生一个新的实例。

以静态内部类方式举例,要解决这个问题,可以通过增加一个特殊的方法 readResolve() 来解决。readResolve() 方法会在反序列化后调用,它允许我们返回原始的单例实例,而不是新创建的实例。


建造者模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图时,我们需要应用于一个设计模式,‘建造者(Builder)模式’,又叫生成器模式。主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

比如创建一个手机,需要cpu,主板、内存、屏幕等,通过一个代码示例可以很清晰的得知建造者模式的用途:

package org.example.pattern.builder3;

public class Phone {
    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }

    //私有构造
    private Phone(Builder builder) {
        this.cpu = builder.cpu;
        this.screen = builder.screen;
        this.memory = builder.memory;
        this.mainboard = builder.mainboard;
    }

    public static final class Builder {
        private String cpu;
        private String screen;
        private String memory;
        private String mainboard;

        public Builder cpu(String cpu) {
            this.cpu = cpu;
            return this;
        }

        public Builder screen(String screen) {
            this.screen = screen;
            return this;
        }

        public Builder memory(String memory) {
            this.memory = memory;
            return this;
        }

        public Builder mainboard(String mainboard) {
            this.mainboard = mainboard;
            return this;
        }

        public Phone build() {
            return new Phone(this);
        }
    }
}
package org.example.pattern.builder3;

public class Test {
    public static void main(String[] args) {
        Phone phone = new Phone.Builder()
                .cpu("intel")
                .mainboard("华硕")
                .memory("金士顿")
                .screen("三星").build();
        System.out.println(phone);
    }
}

原型模式

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型对象相同的新对象 1.如果你需要复制一些对象, 同时又希望代码独立于这些对象所属的具体类, 可以使用原型模式。 2.如果子类的区别仅在于其对象的初始化方式, 那么你可以使用该模式来减少子类的数量。 别人创建这些子类的目的可能是为了创建特定类型的对象。 3.Spring中原型bean的创建,就是原型模式的应用

原型模式的克隆分为浅克隆和深克隆。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原 有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

古老方法

 
/**
 * 羊 实体类
 */
public class Sheep {
 
    // 名称
    private String name;
    // 产地
    public SheepAddress sheepAddress;
 
    public Sheep(){}
 
    public Sheep(String name, SheepAddress sheepAddress) {
        this.name = name;
        this.sheepAddress = sheepAddress;
    }
 
    @Override
    public String toString() {
        return "Sheep{name='" + name + ", sheepAddress=" + sheepAddress +"}";
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(name, sheepAddress);
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public SheepAddress getSheepAddress() {
        return sheepAddress;
    }
 
    public void setSheepAddress(SheepAddress sheepAddress) {
        this.sheepAddress = sheepAddress;
    }
}
/**
 * 羊产地
 */
public class SheepAddress {
 
    //省
    private String province;
    //市
    private String city;
 
    public SheepAddress(){}
 
    public SheepAddress(String province, String city) {
        this.province = province;
        this.city = city;
    }
 
    @Override
    public String toString() {
        return "SheepAddress{province=" + province + ", city='" + city + "}";
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(province, city);
    }
 
    public String getProvince() {
        return province;
    }
 
    public void setProvince(String province) {
        this.province = province;
    }
 
    public String getCity() {
        return city;
    }
 
    public void setCity(String city) {
        this.city = city;
    }
}
public class ProtTest {
 
    public static void main(String[] args) {
        Sheep sheep = new Sheep("多莉", new SheepAddress("内蒙古", "呼和浩特市"));
        System.out.println("sheep对象的hashcode:" + sheep.hashCode() + " sheepAddress对象的hashcode:" + sheep.sheepAddress.hashCode() + " 属性值:" + sheep);
 
        Sheep sheep1 = new Sheep("多莉1", new SheepAddress("内蒙古1", "呼和浩特市1"));
        System.out.println("sheep对象的hashcode:" + sheep1.hashCode() + " sheepAddress对象的hashcode:" + sheep1.sheepAddress.hashCode() + " 属性值:" + sheep1);
 
        Sheep sheep2 = new Sheep("多莉2", new SheepAddress("内蒙古2", "呼和浩特市2"));
        System.out.println("sheep对象的hashcode:" + sheep2.hashCode() + " sheepAddress对象的hashcode:" + sheep2.sheepAddress.hashCode() + " 属性值:" + sheep2);
 
    }
}
sheep对象的hashcode:-150317243 sheepAddress对象的hashcode:-173282477 属性值:Sheep{name='多莉, sheepAddress=SheepAddress{province=内蒙古, city='呼和浩特市}}
sheep对象的hashcode:-364921810 sheepAddress对象的hashcode:-1076816753 属性值:Sheep{name='多莉1, sheepAddress=SheepAddress{province=内蒙古1, city='呼和浩特市1}}
sheep对象的hashcode:-364921747 sheepAddress对象的hashcode:-1076816721 属性值:Sheep{name='多莉2, sheepAddress=SheepAddress{province=内蒙古2, city='呼和浩特市2}}

浅拷贝

/**
 * 原型设计模式:浅拷贝,创建一只多莉羊并实现其Cloneable接口
 **/
public class SheepClone06 implements Cloneable{
 
    // 名称
    private String name;
    // 产地
    public SheepAddress06 sheepAddress;
 
    public SheepClone06(){}
 
    public SheepClone06(String name, SheepAddress06 sheepAddress) {
        this.name = name;
        this.sheepAddress = sheepAddress;
    }
 
    public void printSheep() {
        System.out.println(name + ":" + "是一只克隆羊!");
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(name, sheepAddress);
    }
 
    @Override
    public String toString() {
        return "SheepClone06{name='" + name + ", sheepAddress=" + sheepAddress + "}";
    }
 
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public SheepAddress06 getSheepAddress() {
        return sheepAddress;
    }
 
    public void setSheepAddress(SheepAddress06 sheepAddress) {
        this.sheepAddress = sheepAddress;
    }
}
/**
 * 羊的产区
 */
public class SheepAddress06 {
 
    //省
    private String province;
    //市
    private String city;
 
    public SheepAddress06(){}
 
    public SheepAddress06(String province, String city) {
        this.province = province;
        this.city = city;
    }
 
    @Override
    public String toString() {
        return "SheepAddress06{province='" + province + "', city='" + city + "}";
    }
 
    public String getProvince() {
        return province;
    }
 
    public void setProvince(String province) {
        this.province = province;
    }
 
    public String getCity() {
        return city;
    }
 
    public void setCity(String city) {
        this.city = city;
    }
}
/**
 * 原型设计模式是一种创建型设计模式,允许一个对象再创建另一个可定制的对象,无需知道如何创建对象的细节,
 * springmvc框架中的单例(singleton)与多例(prototype)正是基于该设计模式而设计的。
 * 原型设计模式分为俩种,一种是浅拷贝,另一种是深拷贝。浅拷贝指的是对于基本数据类型和引用类型的变量通过值传递和引用传递,
 * 通俗易懂的说法就是,原对象的任何更改都会影响到克隆对象。而深拷贝是通过完整的克隆,重新创建一个新的对象,
 * 原对象的更改不会影响到克隆对象。浅拷贝通过实现Cloneable接口,重写clone方法实现。
 * 深拷贝可以通过重写clone方法或者实现Serializable序列化接口,通过序列化实现对象深拷贝。
 * 多用于复杂对象的创建,简化对象创建过程。本节我们以克隆多莉羊为例,实现原型模式的一个案例。
 *
 * 原型设计模式:浅拷贝,创建一只多莉羊并实现其Cloneable接口
 */
public class ProtTest06 {
 
    public static void main(String[] args) throws CloneNotSupportedException {
        SheepClone06 sheep = new SheepClone06("多莉", new SheepAddress06("内蒙古", "呼和浩特市"));
        sheep.printSheep();
        System.out.println("sheep对象-->hashcode值:" + sheep.hashCode() + " sheepAddress对象的hashcode值:" + sheep.sheepAddress.hashCode() + " 属性值:" + sheep);
        //调用克隆方法克隆一只多莉羊
        System.out.println();
        System.out.println("开始浅拷贝SheepClone对象...");
        SheepClone06 cloneSheep = (SheepClone06) sheep.clone();
        cloneSheep.printSheep();
        //更改多莉羊的产地
        sheep.getSheepAddress().setProvince("新疆");
        sheep.getSheepAddress().setCity("乌鲁木齐");
        //克隆的多莉羊属性
        System.out.println("克隆sheep对象-->hashcode值:" + cloneSheep.hashCode() + " sheepAddress对象的hashcode值:" + cloneSheep.sheepAddress.hashCode() + " 属性值: " + cloneSheep);
 
        System.out.println();
        System.out.println("两个对象值分别为 sheep=" + sheep +" cloneSheep="+cloneSheep);
    }
 
}

输出结果:

从上面的案例可以看出,修改复制的新对象属性内容,会影响之前的对象属性值。  1) 对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将 该属性值复制一份给新的对象。  2) 对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类 的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成 员变量值  3) 前面我们克隆羊就是浅拷贝  4) 浅拷贝是使用默认的 clone()方法来实现 sheep = (Sheep) super.clone();

深拷贝

 
public class SheepClone07 implements Cloneable, Serializable {
 
    // 名称
    private String name;
    // 产地
    public SheepAddress07 sheepAddress;
 
    public SheepClone07(){}
 
    public SheepClone07(String name, SheepAddress07 sheepAddress) {
        this.name = name;
        this.sheepAddress = sheepAddress;
    }
 
    public void printSheep() {
        System.out.println(name + ":" + "是一只克隆羊!");
    }
 
    @Override
    public int hashCode() {
        return Objects.hash(name, sheepAddress);
    }
 
    @Override
    public String toString() {
        return "SheepClone07{name='" + name + ", sheepAddress=" + sheepAddress + "}";
    }
 
    /**
     * 方式一: 通过重写clone方法实现深拷贝
     **/
    protected Object clone() throws CloneNotSupportedException {
        //完成对属性为基本数据类型和String的克隆
        Object deep = super.clone();
        SheepClone07 sheepClone = (SheepClone07) deep;
        sheepClone.sheepAddress = (SheepAddress07) sheepAddress.clone();
        return sheepClone;
    }
 
 
    /**
     * 方式二: 通过序列化反序列化实现深拷贝(推荐使用)
     **/
    public Object deepClone() {
        //创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            SheepClone07 sheepClone = (SheepClone07) ois.readObject();
            return sheepClone;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            //关闭流
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public SheepAddress07 getSheepAddress() {
        return sheepAddress;
    }
 
    public void setSheepAddress(SheepAddress07 sheepAddress) {
        this.sheepAddress = sheepAddress;
    }
}
public class SheepAddress07 implements Cloneable, Serializable {
 
    //省
    private String province;
    //市
    private String city;
 
    public SheepAddress07(){}
 
    public SheepAddress07(String province, String city) {
        this.province = province;
        this.city = city;
    }
 
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
 
 
    @Override
    public String toString() {
        return "SheepAddress07{province='" + province + "', city='" + city + "}";
    }
 
    public String getProvince() {
        return province;
    }
 
    public void setProvince(String province) {
        this.province = province;
    }
 
    public String getCity() {
        return city;
    }
 
    public void setCity(String city) {
        this.city = city;
    }
}
public class ProtTest07 {
 
    public static void main(String[] args) throws CloneNotSupportedException {
        SheepClone07 sheep = new SheepClone07("多莉", new SheepAddress07("内蒙古", "呼和浩特市"));
        sheep.printSheep();
        System.out.println("sheep对象-->hashcode值:" + sheep.hashCode() + " sheepAddress对象的hashcode值:" + sheep.sheepAddress.hashCode() + " 属性值:" + sheep);
 
        System.out.println();
        System.out.println("开始深拷贝方式一SheepClone对象(通过重写clone方法实现深拷贝)...");
        SheepClone07 cloneSheepOne = (SheepClone07) sheep.clone();
        cloneSheepOne.setName("绵羊");
        cloneSheepOne.printSheep();
        System.out.println("克隆sheep对象-->hashcode值:" + cloneSheepOne.hashCode() + " sheepAddress对象的hashcode值:" + cloneSheepOne.sheepAddress.hashCode() + " 属性值: " + cloneSheepOne);
 
        System.out.println();
        System.out.println("开始深拷贝方式二序列化SheepClone对象(通过序列化反序列化实现深拷贝)...");
        SheepClone07 cloneSheepTwo = (SheepClone07) sheep.deepClone();
        cloneSheepTwo.printSheep();
        System.out.println("克隆sheep对象-->hashcode值:" + cloneSheepTwo.hashCode() + " sheepAddress对象的hashcode值:" + cloneSheepTwo.sheepAddress.hashCode() + " 属性值: " + cloneSheepTwo);
 
        System.out.println();
        System.out.println("两个对象值分别为 sheep=" + sheep +" cloneSheepOne="+cloneSheepOne+" cloneSheepTwo="+cloneSheepTwo);
 
    }
}

输出结果:

深拷贝基本介绍

  1. 复制对象的所有基本数据类型的成员变量值
  2. 为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变 量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对 整个对象进行拷贝
  3. 深拷贝实现方式
  • 重写clone方法来实现深拷贝
  • 通过对象序列化实现深拷贝(推荐)

原型模式的注意事项和细节

  1. 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
  2. 不用重新初始化对象,而是动态地获得对象运行时的状态。
  3. 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化,无需修改代码。
  4. 在实现深克隆的时候可能需要比较复杂的代码。
  5. 缺点:需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有 的类进行改造时,需要修改其源代码,违背了ocp原则。

抽象工厂模式

概述

抽象工厂模式为一个产品家族提供了统一的创建接口。当需要这个产品家族的某一系列的时候,可以从抽象工厂中选出相对应的系列来创建一个具体的工厂类别。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。

相关术语:

  • **产品等级结构:**产品的继承结构,与类的继承相似。例如笔记本是一个抽象的类,那么华为笔记本、苹果和联想笔记本就是其子类。

  • **产品族:**指同一个工厂生产的,位于不同的产品等级结构的一组产品。例如华为笔记本、手机、路由器等都产自华为,笔记本的等级结构不同,构成一个产品族。

  • **抽象工厂:**是一个接口,抽象工厂模式的核心,包含对多个产品等级结构的声明,任何工厂类都必须实现这个接口。

  • **具体工厂:**是抽象工厂的实现,负责实例化某个产品族中的产品对象。例如华为工厂生产华为笔记本、手机、路由器等。

产品族和产品等级结构图

我们以一个品牌为一个产品族,电脑、手机、路由器为产品等级,每一个品牌都有自己的产品族,这就构成一个完整产品群;

横向代表一族,纵向代表一个等级,横纵交集代表某一个品牌的某一个产品(比如下图中交集的点为电脑),请看下图;

UML类图

这个类图其实比较简单,简单说明下:

产品顶级接口:主要被产品抽象类实现;

产品抽象类:某个具体产品要实现的类;

具体实现类:具体产品实现,比如华为路由器实现自抽象类AbstractRouter;

工厂接口:工厂接口中定义创建每个产品方法;

具体华为工厂:实现工厂接口,创建华为一族产品(路由器、手机、电脑);

具体代码实现

代码中我们以华为产品为例,分别定义华为电脑、手机、路由器产品,从UML类图中可以看出我们的产品结构层级比较清晰,现在我们先设计我们产品。

产品顶级接口

package pattern.abstractfactory.product;
/**
 * 定义产品接口
 */
public interface InterfaceProduct {
    void get();
}

定义计算机抽象类并实现产品InterfaceProduct 接口

package pattern.abstractfactory.product;
/**
 * 定义计算机产品抽象类,并实现产品接口InterfaceProduct
 */
public abstract class AbstractComputers implements InterfaceProduct {
    public abstract void get();
}

定义手机抽象类并实现产品InterfaceProduct 接口

package pattern.abstractfactory.product;
/**
 * 定义手机抽象类,并实现产品接口InterfaceProduct
 */
public abstract class AbstractPhone  implements InterfaceProduct {
    public abstract void get();
}

定义路由器抽象类并实现产品InterfaceProduct 接口

package pattern.abstractfactory.product;
/**
 * 定义路由器产品抽象类,并实现InterfaceProduct接口
 */
public abstract class AbstractRouter implements InterfaceProduct{
    public abstract void get();
}

定义华为电脑具体实现类,继承AbstractComputers抽象类

package pattern.abstractfactory.product;
/**
  * 华为电脑实现类
 */
public class HuaWeiComputer extends AbstractComputers{
    @Override
    public void get() {
        System.out.println("华为笔记本");
    }
}

定义华为手机具体实现类,继承AbstractPhone抽象类

package pattern.abstractfactory.product;
/**
  *  华为手机实现类,
 */
public class HuaWeiPhone extends AbstractPhone{
    @Override
    public void get() {
        System.out.println("华为手机");
    }
}

定义华为路由器具体实现类,继承AbstractRouter抽象类

package pattern.abstractfactory.product;
/**
 * 华为路由器
 */
public class HuaWeiRouter extends AbstractRouter {
    @Override
    public void get() {
        System.out.println("华为品牌路由器");
    }
}

下面开始定义工厂

定义工厂接口

package pattern.abstractfactory.factory;
import pattern.abstractfactory.product.InterfaceProduct;
/**
 * 定义产品工厂接口,
 */
public interface InterfactFactory {
    //手机产品
    InterfaceProduct createPhone();
    //电脑产品
    InterfaceProduct createComputer();
    //路由器产品
    InterfaceProduct createRouter();
}

具体工厂实现类,实现 InterfactFactory  接口

package pattern.abstractfactory.factory;
import pattern.abstractfactory.product.HuaWeiComputer;
import pattern.abstractfactory.product.HuaWeiPhone;
import pattern.abstractfactory.product.HuaWeiRouter;
import pattern.abstractfactory.product.InterfaceProduct;
/**
 * **华为工厂**
 */
public class HuaWeiFactory implements InterfactFactory {
    /**
     * 创建电脑对象并返回
     */
    @Override
    public InterfaceProduct createComputer() {
        return new HuaWeiComputer();
    }
    /**
     * 创建手机对象并返回
     */
    @Override
    public InterfaceProduct createPhone() {
        return new HuaWeiPhone();
    }
    /**
     * 创建路由器对象并返回
     */
    @Override
    public InterfaceProduct createRouter() {
        return new HuaWeiRouter();
    }
}

客户端类

package pattern.abstractfactory;
import pattern.abstractfactory.factory.HuaWeiFactory;
import pattern.abstractfactory.factory.InterfactFactory;
import pattern.abstractfactory.product.InterfaceProduct;
/**
 * 抽象工厂模式测试类
 */
public class test {
    public static void main(String[] args) {
        //创建华为品牌工厂
        InterfactFactory huawei = new HuaWeiFactory();
        //通过华为工厂获取华为电脑对象
        InterfaceProduct computer = huawei.createComputer();
        computer.get();
        //通过华为工厂获取华为手机对象
        InterfaceProduct phone = huawei.createPhone();
        phone.get();
        //通过华为工厂获取华为路由器对象
        InterfaceProduct router = huawei.createRouter();
        router.get();
    }
}

输出结果:

扩展展品族

抽象工厂模式对于横向扩展方便,对于纵向扩展非常困难,也就是说:假如我们要扩展一个新的品牌,比如扩展一个小米品牌,小米产品有电脑、手机、路由器,扩展新品牌就是横向扩展,非常方便,但是我们要给小米添加一个电饭煲产品却非常困难,这就是纵向扩展,所以在使用抽象工厂模式时一定要选择合适的场景,也就是在不同场景中使用最适合的模式才是设计模式的精髓。

小米电脑

package pattern.abstractfactory.product;
/**
 * 小米电脑,继承自 AbstractComputers 抽象类
 */
public class MiComputer extends AbstractComputers {
    @Override
    public void get() {
        System.out.println("小米电脑");
    }
}

小米手机

package pattern.abstractfactory.product;
/**
 * 小米手机,继承自 AbstractPhone 抽象类
 */
public class MiPhone extends AbstractPhone {
    @Override
    public void get() {
        System.out.println("小米手机");
    }
}

小米路由器

package pattern.abstractfactory.product;
/**
  * 小米路由器,继承自 AbstractRouter 抽象类
 */
public class MiRouter extends AbstractRouter{
    @Override
    public void get() {
        System.out.println("小米路由器");
    }
}

添加小米具体工厂类

package pattern.abstractfactory.factory;
import pattern.abstractfactory.product.InterfaceProduct;
import pattern.abstractfactory.product.MiComputer;
import pattern.abstractfactory.product.MiPhone;
import pattern.abstractfactory.product.MiRouter;
/**
 * 小米工厂,实现 InterfactFactory 接口
 */
public class MiFactory implements InterfactFactory{
    //小米手机
    @Override
    public InterfaceProduct createPhone() {
        return new MiPhone();
    }
    //小米电脑
    @Override
    public InterfaceProduct createComputer() {
        return new MiComputer();
    }
    //小米路由器
    @Override
    public InterfaceProduct createRouter() {
        return new MiRouter();
    }
}

最后编写测试类,代码中红色字体为新扩展的品牌产品

package pattern.abstractfactory;
import pattern.abstractfactory.factory.HuaWeiFactory;
import pattern.abstractfactory.factory.InterfactFactory;
import pattern.abstractfactory.factory.MiFactory;
import pattern.abstractfactory.product.InterfaceProduct;
/**
 * 抽象工厂模式测试类
 */
public class test {
    public static void main(String[] args) {
        // 创建华为品牌工厂
        InterfactFactory huawei = new HuaWeiFactory();
        // 通过华为工厂获取华为电脑对象
        InterfaceProduct computer = huawei.createComputer();
        computer.get();
        // 通过华为工厂获取华为手机对象
        InterfaceProduct phone = huawei.createPhone();
        phone.get();
        // 通过华为工厂获取华为路由器对象
        InterfaceProduct router = huawei.createRouter();
        router.get();

        // 创建小米品牌工厂
        InterfactFactory Mifactory = new MiFactory();
        // 通过小米工厂获取小米电脑对象
        InterfaceProduct micomputer = Mifactory.createComputer();
        micomputer.get();
        // 通过小米工厂获取小米手机对象
        InterfaceProduct miphone = Mifactory.createPhone();
        miphone.get();
        // 通过小米工厂获取小米路由器对象
        InterfaceProduct mirouter = Mifactory.createRouter();
        mirouter.get();
    }
}

输出结果: