📜  Java中的建造者模式

📅  最后修改于: 2022-05-13 01:55:31.408000             🧑  作者: Mango

Java中的建造者模式

方法链:在Java中,方法链用于在同一个对象上调用多个方法,这些方法作为单个语句出现。方法链由一系列方法实现,这些方法返回类实例的 this 引用。

实现:由于链中方法的返回值是此引用,因此此实现允许我们通过对链中前一个方法的返回值进行下一个方法调用来调用链中的方法。

// Java code to demonstrate method chaining
final class Student {
  
    // instance fields
    private int id;
    private String name;
    private String address;
  
    // Setter Methods
    // Note that all setters method
    // return this reference
    public Student setId(int id)
    {
        this.id = id;
        return this;
    }
  
    public Student setName(String name)
    {
        this.name = name;
        return this;
    }
  
    public Student setAddress(String address)
    {
        this.address = address;
        return this;
    }
  
    @Override
    public String toString()
    {
        return "id = " + this.id + ", name = " + this.name + 
                               ", address = " + this.address;
    }
}
  
// Driver class
public class MethodChainingDemo {
    public static void main(String args[])
    {
        Student student1 = new Student();
        Student student2 = new Student();
  
        student1.setId(1).setName("Ram").setAddress("Noida");
        student2.setId(2).setName("Shyam").setAddress("Delhi");
  
        System.out.println(student1);
        System.out.println(student2);
    }
}

输出:

id = 1, name = Ram, address = Noida
id = 2, name = Shyam, address = Delhi

需要构建器模式:方法链是一种有用的设计模式,但是如果同时访问,线程可能会观察到某些字段包含不一致的值。虽然上面例子中的所有 setter 方法都是原子的,但是方法链中的调用会在对象被并发修改时导致对象状态不一致。下面的示例可以将我们引导到一个不一致状态Student实例,例如,一个名为Ram和地址Delhi的学生。

// Java code to demonstrate need of Builder Pattern
  
// Server Side Code
final class Student {
  
    // instance fields
    private int id;
    private String name;
    private String address;
  
    // Setter Methods
    // Note that all setters method
    // return this reference
    public Student setId(int id)
    {
        this.id = id;
        return this;
    }
  
    public Student setName(String name)
    {
        this.name = name;
        return this;
    }
  
    public Student setAddress(String address)
    {
        this.address = address;
        return this;
    }
  
    @Override
    public String toString()
    {
        return "id = " + this.id + ", name = " + this.name + 
                               ", address = " + this.address;
    }
}
  
// Client Side Code
class StudentReceiver {
  
    private final Student student = new Student();
  
    public StudentReceiver()
    {
  
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run()
            {
                student.setId(1).setName("Ram").setAddress("Noida");
            }
        });
  
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run()
            {
                student.setId(2).setName("Shyam").setAddress("Delhi");
            }
        });
  
        t1.start();
        t2.start();
    }
  
    public Student getStudent()
    {
        return student;
    }
}
  
// Driver class
public class BuilderNeedDemo {
    public static void main(String args[])
    {
        StudentReceiver sr = new StudentReceiver();
        System.out.println(sr.getStudent());
    }
}

输出可能是:

id = 2, name = Shyam, address = Noida

另一个不一致的输出可能是

id = 0, name = null, address = null

注意:尝试在循环中运行main方法语句(即同时向服务器发出多个请求)。

为了解决这个问题,有 Builder 模式来保证对象创建的线程安全原子性

实现:在 Builder 模式中,我们在 Server 类中有一个名为Builder的内部静态类,其中包含该类的实例字段,并且还有一个工厂方法来在每次调用时返回Builder类的实例。 setter 方法现在将返回Builder类引用。我们还将有一个构建方法来返回服务器端类的实例,即外部类。

// Java code to demonstrate Builder Pattern
  
// Server Side Code
final class Student {
  
    // final instance fields
    private final int id;
    private final String name;
    private final String address;
  
    public Student(Builder builder)
    {
        this.id = builder.id;
        this.name = builder.name;
        this.address = builder.address;
    }
  
    // Static class Builder
    public static class Builder {
  
        /// instance fields
        private int id;
        private String name;
        private String address;
  
        public static Builder newInstance()
        {
            return new Builder();
        }
  
        private Builder() {}
  
        // Setter methods
        public Builder setId(int id)
        {
            this.id = id;
            return this;
        }
        public Builder setName(String name)
        {
            this.name = name;
            return this;
        }
        public Builder setAddress(String address)
        {
            this.address = address;
            return this;
        }
  
        // build method to deal with outer class
        // to return outer instance
        public Student build()
        {
            return new Student(this);
        }
    }
  
    @Override
    public String toString()
    {
        return "id = " + this.id + ", name = " + this.name + 
                               ", address = " + this.address;
    }
}
  
// Client Side Code
class StudentReceiver {
  
    // volatile student instance to ensure visibility
    // of shared reference to immutable objects
    private volatile Student student;
  
    public StudentReceiver()
    {
  
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run()
            {
                student = Student.Builder.newInstance()
                              .setId(1)
                              .setName("Ram")
                              .setAddress("Noida")
                              .build();
            }
        });
  
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run()
            {
                student = Student.Builder.newInstance()
                              .setId(2)
                              .setName("Shyam")
                              .setAddress("Delhi")
                              .build();
            }
        });
  
        t1.start();
        t2.start();
    }
  
    public Student getStudent()
    {
        return student;
    }
}
  
// Driver class
public class BuilderDemo {
    public static void main(String args[])
    {
        StudentReceiver sr = new StudentReceiver();
        System.out.println(sr.getStudent());
    }
}

输出保证为以下之一:

id = 1, name = Ram, address = Noida

或者

id = 2, name = Shyam, address = Delhi

也可以使用任何必需的参数调用Builder.newInstance()工厂方法,以通过重载获取Builder实例。 Student类的对象是通过调用build()方法构造的。
Builder 模式的上述实现使Student不可变,因此是线程安全的

另请注意,客户端代码中的student字段不能声明为 final,因为它被分配了一个新的不可变对象。但它被声明为 volatile 以确保对不可变对象的共享引用的可见性。 Builder类的私有成员也维护封装。

请查看Java.lang包中StringBuilder类的 append 方法,以进一步了解 Builder 模式的实现。