lengjingbuliaoyidian
文章15
标签7
分类7
建造者模式

建造者模式

建造者模式(Builder Pattern)

  • 概述:主要目的是将一个复杂对象的构建过程与其表示相分离,从而可以创建具有不同表示形式的对象。(使得同样的构造过程可以创建不同的表示)

结构:

建造者模式包含以下几个主要角色:

  1. 抽象建造者(Builder):规定了实现复杂对象的哪些部分的创建,并不设计具体的部件对象的创建。

  2. 具体建造者(Concrete Builder):实现抽象建造者接口,完成复杂产品的各个部件的具体创建方法。在构建过程完成之后,提供产品的实例。

  3. 产品(Product):要构建的复杂对象。产品类通常包含多个部分或属性。

  4. 指导者(Director):负责调用具体建造者的方法来构建产品,指导者并不了解具体的构建过程,只关心产品的构建顺序和方式

-Bike产品.java

public class Bike{
private String frame;
private String seat;

public String getFrame(){
return frame;
}
public void setFrame(String frame){
this.frame=frame;
}
public String getSeat(){
return seat;
}
public void setSeat(String seat){
this.seat=seat;
}
}
  • Builder抽象建造者.java

    public abstract class Builder{

    protected Bike bike=new Bike();

    public abstract void buildFrame();
    public abstract void buildSeat();

    //返回建造产品

    public abstract Bike createBike();

    }
  • 具体建造者1.java

        public class MobikeBuider extends Builder {
    @Override
    public void builerFrame() {
    bike.setFrame("摩拜碳钎维车架");
    }

    @Override
    public void builerSeat() {
    bike.setSeat("摩拜真皮车座");

    }

    @Override
    public Bike createBike() {
    return bike;
    }
    }
  • 具体建造者2.java

    public class OfoBuilder extends Builder{
    @Override
    public void builerFrame() {
    bike.setFrame("Ofo铝合金车架");
    }

    @Override
    public void builerSeat() {
    bike.setSeat("Ofo橡皮车座");

    }

    @Override
    public Bike createBike() {
    return bike;
    }
    }
  • 指导者(Director).java

    private Builder builder;

    public Direactor(Builder builder){
    this.builder=builder;
    }
    public Bike construct(){
    builder.buildFrame();
    builder.buildSeat();
    return builder.createBike();

    }
  • 总结:

  1. 指挥者用于指导具体构建者如何构建产品,控制调用的先后顺序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,就可以把指挥者类和抽象建造者进行合并。
   public  abstract class Builder {
/*
Builder 构造器相当于一个构建成品的骨架蓝图
*/
//声明Bike类型的变量
protected Bike bike = new Bike();

public abstract void builerFrame();

public abstract void builerSeat();

public abstract Bike createBike();

public Bike build(){
this.builerFrame();
this.builerSeat();
return this.createBike();
}
}
  1. 这样做确实简化了系统结构,但是同时家中了抽象建造者类的职责,也不太符合单一职责原则,如果construct过于复杂,建议还是封装到Director中。

使用场景:

  • 需要生成的对象具有复杂的内部结构。
  • 需要生成的对象内部属性相互依赖。

优点

分离构建过程和表示,使得构建过程更加灵活,可以构建不同的表示。
可以更好地控制构建过程,隐藏具体构建细节。
代码复用性高,可以在不同的构建过程中重复使用相同的建造者。

缺点

如果产品的属性较少,建造者模式可能会导致代码冗余。
增加了系统的类和对象数量。

原型模式

原型模式

原型模式(Prototype Pattern)

  • 概述:
    原型模式(Prototype Pattern)是创建型模式,用于当创建对象的成本高时,通过复制一个现有对象来创建一个新对象,从而避免创建一个新对象的 。
    这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
  • 结构:
    • 抽象原型类:定义了一个克隆自身的接口,即规定了具体原型对象必须实现的clone()方法。
    • 具体原型类:实现抽象原型类的clone()方法,它是可被复制的对象。
    • 客户端类:使用具体原型类中的clone()方法来克隆新的对象。
  • 实现:
    原型模式的克隆分为:浅克隆和深克隆。
  1. 浅克隆创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,任指向原有属性所指向的对象的内存地址。
  2. 深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不会指向原有对象地址。
  • 注意:Java中的Object类提供了clone()方法,该方法返回一个对象,该对象是当前对象的一个副本。
    • 浅克隆:调用Object类的clone()方法。
    • 深克隆:
      1. 实现Cloneable接口。
      2. 重写clone()方法。
      3. 在clone()方法中,使用super.clone()方法创建一个新对象,然后将当前对象的属性值复制到新对象中。
      4. 如果属性是引用类型,需要递归调用clone()方法。

在Java中,原型模式(Prototype Pattern)是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是通过新建类实例的方式。在实现原型模式时,我们通常会使用clone()方法,而复制对象时就会涉及到浅克隆(Shallow Clone)和深克隆(Deep Clone)的概念。

浅克隆(Shallow Clone)

浅克隆是指当复制一个对象时,对于基本数据类型的成员变量,会直接复制其值;而对于引用类型的成员变量,则只复制其引用地址(也就是内存地址),而不会复制引用所指向的实际对象。因此,在浅克隆中,原始对象和克隆对象中的引用类型成员变量将指向同一个对象。
在Java中,通过实现Cloneable接口并重写Object类的clone()方法来实现浅克隆。Object类的clone()方法默认实现就是浅克隆。
示例代码:

class Address {
private String city;
public Address(String city) {
this.city = city;
}
// getter and setter
}
class Person implements Cloneable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// getter and setter
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // 浅克隆
}
}
// 使用
Address address = new Address("Beijing");
Person p1 = new Person("Tom", address);
Person p2 = (Person) p1.clone();
// 此时,p1和p2的address引用指向同一个Address对象
System.out.println(p1.getAddress() == p2.getAddress()); // true

深克隆(Deep Clone)

深克隆是指复制对象时,不仅复制对象本身,而且递归复制对象所引用的其他对象。因此,深克隆会复制整个对象网络,使得原始对象和克隆对象之间互不影响,它们拥有各自独立的内存空间。
实现深克隆的方式有多种:

  1. 重写clone()方法,并在其中对引用类型进行递归克隆(需要引用类型也实现Cloneable接口并重写clone()方法)。
  2. 使用序列化(Serialization)和反序列化(Deserialization)来实现深克隆。将对象写入到字节流中,然后再从字节流中读取回来,这样会创建一个完全独立的副本。
    示例代码(通过重写clone方法实现深克隆):
    class Address implements Cloneable {
    private String city;
    public Address(String city) {
    this.city = city;
    }
    // getter and setter
    @Override
    protected Object clone() throws CloneNotSupportedException {
    return super.clone();
    }
    }
    class Person implements Cloneable {
    private String name;
    private Address address;
    public Person(String name, Address address) {
    this.name = name;
    this.address = address;
    }
    // getter and setter
    @Override
    protected Object clone() throws CloneNotSupportedException {
    Person cloned = (Person) super.clone();
    // 对引用类型单独克隆
    cloned.address = (Address) address.clone();
    return cloned;
    }
    }
    // 使用
    Address address = new Address("Beijing");
    Person p1 = new Person("Tom", address);
    Person p2 = (Person) p1.clone();
    // 此时,p1和p2的address引用指向不同的Address对象
    System.out.println(p1.getAddress() == p2.getAddress()); // false

序列化实现深克隆

如果引用类型嵌套层次很深,或者引用类型没有实现Cloneable接口,则可以通过序列化的方式实现深克隆。需要确保所有涉及的类都是可序列化的(实现Serializable接口)。
示例代码:

import java.io.*;
class Address implements Serializable {
private String city;
public Address(String city) {
this.city = city;
}
// getter and setter
}
class Person implements Serializable {
private String name;
private Address address;
public Person(String name, Address address) {
this.name = name;
this.address = address;
}
// getter and setter
public Person deepClone() throws IOException, ClassNotFoundException {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Person) ois.readObject();
}
}
// 使用
Address address = new Address("Beijing");
Person p1 = new Person("Tom", address);
Person p2 = p1.deepClone();
System.out.println(p1.getAddress() == p2.getAddress()); // false

总结

  • 浅克隆:复制基本数据类型的值,对于引用类型,只复制引用地址,因此原对象和克隆对象会共享引用类型的成员变量。修改其中一个对象的引用类型成员变量,另一个对象也会受到影响。
  • 深克隆:复制基本数据类型的值,同时递归复制引用类型的对象。因此,原对象和克隆对象之间完全独立,互不影响。
    在实际应用中,选择浅克隆还是深克隆取决于具体需求。如果对象的引用类型成员变量在创建后不会改变,或者你希望共享这些对象,那么浅克隆就足够了,并且效率更高。如果需要完全独立的副本,则应该使用深克隆。但要注意深克隆可能带来的性能问题,特别是在对象图很大的情况下。

关键区别总结

特性 浅克隆 深克隆
引用类型复制 复制内存地址(共享对象) 递归复制实际对象(完全独立)
修改影响 修改引用类型成员会影响所有副本 修改引用类型成员不影响其他副本
实现复杂度 简单(默认clone() 复杂(需递归处理所有引用类型)
性能 高效(不创建新对象) 较低(递归创建新对象)
适用场景 引用类型不可变或无需隔离时 引用类型需完全隔离时

注意事项

  1. String的特殊性
    虽然String是引用类型,但其不可变性(Immutable)使得浅克隆中修改String值会创建新对象,不会影响原对象(行为类似深克隆)。但其他引用类型(如自定义类)仍需谨慎。
  2. 深克隆的替代方案
    序列化(Serializable)是另一种深克隆实现,但要求所有涉及的对象都实现Serializable接口:
    public static <T> T deepClone(T obj) throws IOException, ClassNotFoundException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(obj);

    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    ObjectInputStream ois = new ObjectInputStream(bis);
    return (T) ois.readObject();
    }
单例模式

单例模式

Singleton(单例模式)

  1. 意图:
    确保一个类只有一个实例,并提供一个全局访问点来访问该实例

  2. 注意事项

  • 线程安全:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成实例被多次创建。
  • 延迟初始化:实例在第一次调用 getInstance() 方法时创建。
  • 序列化和反序列化:重写 readResolve 方法以确保反序列化时不会创建新的实例。
  • 反射攻击:在构造函数中添加防护代码,防止通过反射创建新实例。
  • 类加载器问题:注意复杂类加载环境可能导致的多个实例问题。
  1. 结构
    单例模式包含以下几个主要角色:
  • 单例类:包含单例实例的类,通常将构造函数声明为私有。
  • 静态成员变量:用于存储单例实例的静态成员变量。
  • 获取实例方法:静态方法,用于获取单例实例。
  • 私有构造函数:防止外部直接实例化单例类。
  • 线程安全处理:确保在多线程环境下单例实例的创建是安全的。

单例模式的实现

  1. 单例设计模式分类两种:
  • 饿汉式:类加载就会导致该单例实例对象被创建,适用于单线程环境。
  • 懒汉式:类加载不会导致该单例实例对象被创建,而是首次使用该对象时才会创建,适用于多线程环境。

饿汉式——静态成员变量

public class Singleton{
//1. 私有构造方法
private Singleton(){}

//2. 在本类中创建本类对象
private static Singleton instance =new Singleton();

//3. 提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
return instance;
}
}

测试类

public calss Client{
public static void main(String[] args) {
//创建Singleton对象
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
//判断获取到的两个对象是否是同一个对象
if(instance==instance1){
System.out.println("两个对象是同一个对象");
}else{
System.out.println("两个对象不是同一个对象");
}
}
}
//结果: 两个对象是同一个对象
//原因: 饿汉式是在类加载时就创建了实例对象,所以在多线程环境下也不会出现多个实例的情况。

饿汉式——静态代码块

public class Singleton{
//1. 私有构造方法
private Singleton(){}

//2. 先申明Singleton对象
private static Singleton instance;//null

//3. 在静态代码块中进行赋值——创建Singleton对象
static{
instance = new Singleton();
}

//3. 提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
return instance;
}
}

饿汉式——枚举

  • 原由:枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会加载一次。而且枚举类型是所用单例实现中唯一不会被破坏的单例实现模式。
public enum Singleton{
INSTANCE;
}

测试类

 public class Client{
public static void main(String[] args){
//创建Singleton对象
Singleton instance = Singleton.INSTANCE;
Singleton instance1 = Singleton.INSTANCE;
System.out.println(instance==instance1);
}
}
// 结果为true,说明是同一个对象
  • 总结:
    优点: 内存中只有一个实例,减少内存开销,尤其是频繁创建和销毁实例时(如管理学院首页页面缓存)。
    避免资源的多重占用(如写文件操作)。
    缺点:饿汉式在类加载时就创建了实例对象,可能造成内存的浪费。

懒汉式——线程不安全情况

public class Singleton{
//1. 私有构造方法
private Singleton(){}

//2. 先申明Singleton对象
private static Singleton instance;//null

//3. 提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
//首先判断instance是否为null,如果为null,说明还没有创建实例对象,则创建
if(instance==null){
//当有两个线程同时调用getInstance()方法时,线程1等待,线程2获取到了cpu的执行权,也会进入判断里面————》可能会创建多个实例对象,因此需要使用同步锁synchronized --》public static synchronized Singleton getInstance()
instance = new Singleton();
}
//如果instance不为null,则直接返回
return instance;
}
}

测试类


public class Client{
public static void main(String[] args){
//创建Singleton对象
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
//判断获取到的两个对象是否是同一个对象
System.out.println(instance==instance1);
}
}
//结果为ture

懒汉式——线程安全情况

public class Singleton{
//1. 私有构造方法
private Singleton(){}

//2. 先申明Singleton对象
private static Singleton instance;//null

//3. 提供一个公共的访问方式,让外界获取该对象,同时加上synchronized锁保证线程的安全
public static synchronized Singleton getInstance(){
//首先判断instance是否为null,如果为null,说明还没有创建实例对象,则创建
if(instance==null){

instance = new Singleton();
}
//如果instance不为null,则直接返回
return instance;
}
}

懒汉式——双重检查锁

  • 优势:解决了单例、性能、线程安全问题。因为在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重新排序操作。可以使用 volatile 关键字修饰成员变量和静态成员变量,防止指令重排序。
public class Singleton{
//1. 私有构造方法
private Singleton(){}

//2.声明Singleton对象
public static volatile Singleton instance;

//3.提供一个公共的访问方式,让外界获取该对象
public static Singleton getInstance(){
//第一次判断,如果instance不为null,则直接返回
if(instance==null){
synchronized (Singleton.class){
//第二次判断,如果instance不为null,则直接返回,为空就创建该对象
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}

懒汉式——静态内部类

  • 原由:静态内部类单里模式由内部类创建,由于JVM在加载外部类的过程中,是不会加载静态内部类的,只有内部类的属性、方法被调用时才会被加载,并初始化其静态资源,静态资源由于被static修饰,因此只会被创建一次,并且创建时是线程安全的。
public class Singleton{
//1.私有构造方法
private Singleton(){}
//2.定义一个静态内部类
public static class SingletonHolder{
//在内部类中声明并初始化外部类的对象
private static final Singleton INSTANCE = new Singleton();
}
//3.提供公共的访问方式
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}

测试类


public class Client{
public static void main(String[] args){
//创建Singleton对象
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
//判断获取到的两个对象是否是同一个对象
System.out.println(instance==instance1);
}
}
结果为ture
  • 说明:第一次加载Singleton类时并不会初始化instance,只有第一次调用getInstance()方法时,虚拟机加载SingletonHolder类并初始化instance,并且instance是static的,因此只会被创建一次。这样不仅能保证线程的安全性,还能保证Singleton类的唯一性。

存在的问题——破坏单例模式

破会啊单例模式:使上面定义的单例类可以创建多个对象,美剧方式除外,有两种方式:序列化和反射。
例子:

  1. 序列化与反序列化
public class Singleton implements Serializable{
//1.私有构造方法
private Singleton(){}
//2.定义一个静态内部类
public static class SingletonHolder{
//在内部类中声明并初始化外部类的对象
private static final Singleton INSTANCE = new Singleton();
}
//3.提供公共的访问方式
public static Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}

测试类:

public class Client{
public static void main(String[] args) throws Exception {
//writeObject2File(); //执行结果为:C:\\Users\\j.txt 文件创建成功
readObjectFromFile(); //执行结果为:地址1
readObjectF romFile(); //执行结果为:地址2
//说明:序列化和反序列化后的对象不是同一个对象,因此破坏了单例模式。
}
//从文件中读取数据(对象)
public static void readObjectFromFile() throw Exception{
//1.创建输入流对象
ObjectInputStream ois =new ObjectInputStream(new FileInputStream("C:\\Users\\j.txt"));
//2.读取对象
Singleton instance=(Singleton) ois.readObject();
System.out.println(instance);
//3。释放资源
ois.close();
}
//从文件中写数据(对象)
public static void writeObject2File() throws Exception{
//1.获取Singleton对象
Singleton instance = Singleton.getInstance();
//2.创建输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\j.txt"));
//3.写对象
oos.writeObject(instance);
//4.释放资源
oos.close();
}
}
  1. 反射
    public class Client{
    public static void main(String[] args) throws Exception{
    //1.获取Singleton的Class字节码对象
    Class class =Singleton.class;
    //2.获取无参构造方法对象
    Constructor constructor = class.getDeclaredConstructor();
    //3.取消访问检查
    constructor.setAccessible(true);
    //4.通过无参构造方法创建对象
    Singleton instance1 = (Singleton) constructor.newInstance();
    Singleton instance2 = (Singleton) constructor.newInstance();
    //5.判断两个对象是否相等,如果相等,说明反射成功,如果不相等,说明反射失败(破坏了单例模式)
    System.out.println(instance1 == instance2);
    }
    //结果为false
    }

上述问题的解决

1.序列化与反序列化破坏单例模式的解决方法:

  • 在Singleton类中添加readResolve()方法,在序列化时被反射调用,如果定义了这个方法,就返回这个方法的值,反之,就返回新new出来的对象。
    public class Singleton implements Serializable{
    //1.私有构造方法
    private Singleton(){}
    //2.定义一个静态内部类
    public static class SingletonHolder{
    //在内部类中声明并初始化外部类的对象
    private static final Singleton INSTANCE = new Singleton();
    }
    //3.提供公共的访问方式
    public static Singleton getInstance(){
    return SingletonHolder.INSTANCE;
    }
    //4.当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
    public Object readResolve(){
    return SingletonHolder.INSTANCE;
    }
    }

    测试类:
    public class Client{
    public static void main(String[] args) throws Exception {
    //writeObject2File(); //执行结果为:C:\\Users\\j.txt 文件创建成功
    readObjectFromFile(); //执行结果为:地址1
    readObjectF romFile(); //执行结果为:地址1
    //说明:创建对象时,使用的是同一个对象
    }
    //从文件中读取数据(对象)
    public static void readObjectFromFile() throw Exception{
    //1.创建输入流对象
    ObjectInputStream ois =new ObjectInputStream(new FileInputStream("C:\\Users\\j.txt"));
    //2.读取对象
    Singleton instance=(Singleton) ois.readObject();
    System.out.println(instance);
    //3。释放资源
    ois.close();
    }
    //从文件中写数据(对象)
    public static void writeObject2File() throws Exception{
    //1.获取Singleton对象
    Singleton instance = Singleton.getInstance();
    //2.创建输出流对象
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\j.txt"));
    //3.写对象
    oos.writeObject(instance);
    //4.释放资源
    oos.close();
    }
    }
    2.反射破坏单例模式的解决方法:
    public class Singleton {
    private static boolean flag = false;

    //1.私有构造方法
    private Singleton(){

    synchronized (Singleton.class) {
    //判断flag的值是否为true,
    //如果为true,说明已经创建了对象,直接抛出异常
    //如果为false,说明没有创建对象,创建对象
    if (flag) {
    throw new RuntimeException("不能创建多个对象");
    }
    flag = true;
    }
    }

    //2.定义一个静态内部类
    public static class SingletonHolder{
    //在内部类中声明并初始化外部类的对象
    private static final Singleton INSTANCE = new Singleton();
    }

    //3.提供公共的访问方式
    public static Singleton getInstance(){
    return SingletonHolder.INSTANCE;
    }
    }

    public class Client{
    public static void main(String[] args) throws Exception{
    //1.获取Singleton的Class字节码对象
    Class class =Singleton.class;
    //2.获取无参构造方法对象
    Constructor constructor = class.getDeclaredConstructor();
    //3.取消访问检查
    constructor.setAccessible(true);
    //4.通过无参构造方法创建对象
    Singleton instance1 = (Singleton) constructor.newInstance(); //正常
    Singleton instance2 = (Singleton) constructor.newInstance(); //抛出异常:不能创建多个对象
    //5.判断两个对象是否相等,如果相等,说明反射成功,如果不相等,说明反射失败(破坏了单例模式)
    System.out.println(instance1 == instance2);
    }

    }
工厂模式

工厂模式

工厂模式(Factory Pattern)

在Java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,又假如我们要更换对象,就要将所有new对象的地方都要需改一遍,这显然违背了软件设计的开闭原则。如果我们使用工厂来生产对象,我们就只要和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂中更换对象即可,达到了与对象解耦的目的。所以说,工厂模式最大的优点是:减低耦合。

工厂模式类型

  1. 简单工厂模式(Simple Factory Pattern):不属于23中设计模式,因为违反了开闭原则

简单工厂模式不是一个正式的设计模式,但它是工厂模式的基础。它使用一个单独的工厂类来创建不同的对象,根据传入的参数决定创建哪种类型的对象。

  1. 工厂方法模式(Factory Method Pattern):

工厂方法模式定义了一个创建对象的接口,但由子类决定实例化哪个类。工厂方法将对象的创建延迟到子类。

  1. 抽象工厂模式(Abstract Factory Pattern):

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

简单工厂模式:

  • 结构:
  • 抽象产品:定义了产品的规范,描述了产品的主要特征和功能
  • 具体产品:实现了抽象产品接口,定义了产品的具体功能(实现或者继承了抽象产品的子类)
  • 具体工厂:提供了创建产品的方法,调用者通过该方法来创建产品
  • 步骤 1:
    创建一个接口:Shape.java
    public interface Shape{
    void Shape();
    }
  • 步骤 2:
    创建实现接口的实体类。
    Rectangle.java
    public class Restangle implements Shape{
    @Override
    public void Shape(){
    System.out.println("Rectangle");
    }
    }

Square.java

public class Square implements Shape{
@Override
public void Shape(){
System.out.println("Square");
}
}
  • 步骤 3:
    创建一个工厂,生成基于给定信息的实体类的对象。
    ShapeFactory.java
    public calss ShapeFactory{

    //使用getShape()方法获取到形状类型的对象
    public Shape getShape(String shapeType){
    if(shapeTuype==null){
    return null;
    }
    if(shapeType.equalsIgnoreCase("Rectangle")){
    return new Restangle();
    }
    if(shapeType.equalsIgnoreCase("Square")){
    return new Square();
    }

    }
    }
  • 步骤4:
    使用该工厂,通过传递类型信息来获取实体类对象
    FactoryPatternDemo.java
    public class FactoryPatternDemo{
    public static void main(String[] args){
    ShapeFactory shapeFactory =new ShapeFactory();
    //获取矩形对象,并调用绘制方法
    Shape shape1=shapeFactory.getShape("Rectangle");
    shape1.Shape();

    }
    }
    //结果是:Rectangle

工厂方法模式

  • 结构:
    工厂模式主要包含一下几个主要角色:

  • 抽象产品:定义产品的共同接口或抽象类。它可以是具体产品类的父类或接口,规定了该产品对象的共同方法。

  • 具体产品:实现了抽象产品接口,定义了具体产品的特定行为和属性。

  • 抽象工厂:声明了创建产品的抽象方法,可以是接口或者抽象类,它可以有多个方法用于创建不同类型的产品。

  • 具体工厂:实现了抽象工厂接口,定义了创建具体产品的方法,并返回具体产品对象。

  • 抽象工厂:

    public interface CoffeeFactory{
    //创建Coffee对象的方法
    Coffee createCoffee();

    }
  • 具体工厂:

    public class AmericanCoffeeFactory implements CoffeeFactory{
    @Override
    public Coffee createCoffee(){
    return new AmericanCoffee();
    }

    }
    public class LatteCoffeeFactory implements CoffeeFactory{
    @Override
    public Coffee createCoffee(){
    return new LatteCoffee();
    }
    }
  • 抽象产品:

    public abstract class Coffee{
    // 抽象方法声明
    public abstract String getName();
    public void addMilk(){
    System.out.println("加奶");
    }
    public void addSugar(){
    System.out.println("加糖");
    }

    }
  • 具体产品:

    public class AmericanCoffee extends Coffee{

    public String getName(){
    return "AmericanCoffee";
    }
    }
    public class LatteCoffee extends Coffee{

    public String getName(){
    return "LatteCoffee";
    }
    }

    中间依赖类:

    public class CoffeeStore{

    private CoffeeFactory factory;

    public void SetFactory(CoffeeFactory factory){
    this.factory=factory;
    }
    //实现点Coffee功能
    public Coffee OrderCoffee(){
    Coffee coffee=factory.CreateCoffee();
    coffee.addMilk();
    coffee.addSugar();
    return coffee;
    }

    }

    Client 测试类

    public class Client{
    public static void main(String[] args){
    //创建咖啡店对象
    CoffeeStore store=new CoffeeStore();
    //创建咖啡工厂对象
    CoffeeFactory factory=new AmericanCoffeeFactory();
    //设置咖啡工厂对象
    store.SetFactory(factory);
    //点咖啡
    Coffee coffee=store.OrderCoffee();

    System.out.println(coffee.getName());
    }
    }
    //结果是:加奶 加糖 AmericanCoffee
  • 工厂方法模式优势:

  1. 解耦:客户端与具体产品类解耦,调用者只需要知道对象的名称即可创建对象。

  2. 扩展性:添加新产品只需新增具体工厂和产品类

  3. 单一职责:每个工厂只负责创建一种产品

  4. 开闭原则:对扩展开放,对修改关闭

  • 工厂方法模式缺点:
  1. 每次增加一个产品时,都需要增加一个具体类和对应的工厂,使系统中类的数量成倍增加,增加了系统的复杂度和具体类的依赖。

抽象工厂模式

抽象工厂模式(Abstract Factory Pattern)是围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

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

抽象工厂模式提供了一种创建一系列相关或相互依赖对象的接口,而无需指定具体实现类。通过使用抽象工厂模式,可以将客户端与具体产品的创建过程解耦,使得客户端可以通过工厂接口来创建一族产品。

抽象工厂模式通常涉及一族相关的产品,每个具体工厂类负责创建该族中的具体产品。客户端通过使用抽象工厂接口来创建产品对象,而不需要直接使用具体产品的实现类。

  • 结构:
    抽象工厂模式的主要角色如下:

  • 抽象工厂:提供了创建产品的接口,它包含了多个创建产品的方法,可以创建多个不同等级的产品。

  • 具体工厂:主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。

  • 抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。

  • 具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。

  • 优点:
    确保同一产品族的对象一起工作。
    客户端不需要知道每个对象的具体类,简化了代码。

  • 缺点:
    扩展产品族非常困难。增加一个新的产品族需要修改抽象工厂和所有具体工厂的代码。

  • 适用场景:
    1.当系统需要创建多个相关或依赖的对象,而不需要指定具体类时。
    2.当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、电冰箱、空调等。
    3.系统中有多个产品族,而系统只消费其中的一族产品。
    4.当一个产品族中的多个对象被设计成一起工作时,需要确保客户端代码不会依赖于具体的产品类。

  • 抽象工厂:

          
    public interface DessertFactory {
    //生产咖啡的功能
    Coffee createCoffee();
    //生产甜品的功能
    Dessert createDessert();
    }
  • 具体工厂:

    //抽象工厂模式 美式风味的甜品工厂:美式咖啡、抹茶慕斯
    public class AmerricanDessertFactory implements DessertFactory {


    @Override
    public Coffee createCoffee() {
    return new AmericanCoffee();
    }

    @Override
    public Dessert createDessert() {
    return new MatchMousse();
    }
    }
//意大利风味甜品工厂:创建意大利风味的咖啡和甜品
public class ItalyDessertFactory implements DessertFactory{

@Override
public Coffee createCoffee() {
return new LatteCoffee();
}

@Override
public Dessert createDessert() {
return new Trimisu();
}
}
  • 抽象产品:
    public abstract class Dessert {
    public abstract void show();
    }
public abstract class Coffee{
// 抽象方法声明
public abstract String getName();
public void addMilk(){
System.out.println("加奶");
}
public void addSugar(){
System.out.println("加糖");
}

}
  • 具体产品:
    public class Trimisu extends Dessert{
    @Override
    public void show() {
    System.out.println("Trimisu");
    }
    }
    public class MatchMousse extends Dessert{
    @Override
    public void show() {
    System.out.println("MatchMousse");
    }
    }
public class AmericanCoffee extends Coffee{

public String getName(){
return "AmericanCoffee";
}
}
public class LatteCoffee extends Coffee{

public String getName(){
return "LatteCoffee";
}
}
  • 测试类:
    public class Client {
    public static void main(String[] args) {
    //创建的是意大利风味甜品工厂对象
    ItalyDessertFactory factory=new ItalyDessertFactory();
    //再从工厂里面获取所需要的产品
    Coffee coffee=factory.createCoffee();
    Dessert dessert=factory.createDessert();

    System.out.println(coffee.getName());
    dessert.show();

    }
    }

    //结果为:LatteCoffee Trimisu

模式扩展:

简单工厂+配置文件接触耦合:
可以通过工厂模式+配置文件的方式可解除工厂对象和产品对象的耦合,在工厂类中加载配置文件的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。

  • 第一步:定义配置文件
    创建一个配置文件,将产品类全限定名保存在文件中,文件名任意,如:bean.properties
    <bean.properties>
    coffee=com.liuyue.factorymethod.Coffee
    dessert=com.liuyue.factorymethod.Dessert
    american=com.liuyue.factorymethod.AmericanCoffee
    latte=com.liuyue.factorymethod.LatteCoffee
    match=com.liuyue.factorymethod.MatchMousse
    trimisu=com.liuyue.factorymethod.Trimisu
    <bean.properties>

  • 第二部:改进工厂类:

        public class DessertFactory {
            public static Dessert createDessert(String type){
                try {
                    //加载配置文件
                    Properties properties=new Properties();
                    properties.load(new FileInputStream("bean.properties"));
                    //获取type全限定类名
                    String className=properties.getProperty(type);
                    //通过反射创建对象
                    return (Dessert) Class.forName(className).newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }
            public static Coffee createCoffee(String type){
                try {
                    //加载配置文件
                    Properties properties=new Properties();
                    properties.load(new FileInputStream("bean.properties"));
                    //获取type全限定类名
                    String className=properties.getProperty(type);
                    //通过反射创建对象
                    return (Coffee) Class.forName(className).newInstance();
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            }
        }
        //测试类
        public class Client {
            public static void main(String[] args) {
                //创建的是意大利风味甜品工厂对象
                ItalyDessertFactory factory=new ItalyDessertFactory();
                //再从工厂里面获取所需要的产品
                Coffee  coffee=factory.createCoffee();
                Dessert dessert=factory.createDessert();

                System.out.println(coffee.getName());
                dessert.show();
                //通过简单工厂+配置文件的方式创建对象
                Coffee coffee1=DessertFactory.createCoffee("latte");
                Dessert dessert1=DessertFactory.createDessert("trimisu");
                System.out.println(coffee1.getName());
                dessert1.show();
            }
        }