构建者(Builder)设计模式(又叫生成器设计模式):
使用场景
相同的方法,不同执行顺序,产生不同事件结果
多个部件都可以装配到一个对象中,但产生的运行结果不相同
产品类非常复杂或者产品类因为调用顺序不同而产生不同作用
初始化一个对象时,参数过多,或者很多参数具有默认值 ,需要管理一下这个类中的数据
public class Student {
private int id;
private String name;
private String passwd;
private String sex;
private String address;
// 构造器尽量缩小范围
private Student() {
}
// 构造器尽量缩小范围
private Student(Student origin) {
// 拷贝一份
this.id = origin.id;
this.name = origin.name;
this.passwd = origin.passwd;
this.sex = origin.sex;
this.address = origin.address;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getPasswd() {
return passwd;
}
public String getSex() {
return sex;
}
public String getAddress() {
return address;
}
/**
* Student的创建完全依靠Student.Builder,使用一种方法链的方式来创建
*
*/
public static class Builder {
private Student target;
public Builder() {
target = new Student();
}
public Builder address(int id) {
target.id = id;
return this;
}
public Builder name(String name) {
target.name = name;
return this;
}
public Builder password(String passwd) {
target.passwd = passwd;
return this;
}
public Builder sex(String sex) {
target.sex = sex;
return this;
}
public Builder address(String address) {
target.address = address;
return this;
}
public Student build() {
return new Student(target);
}
}
}
Student并不是直接new出来的,对其构造器进行了处理使其可访问范围尽可能的小,只让它通过Student.Builder来构建自己,在Student.Builder中提供了一种类set的方法链的方式来设置值,然后在最后的build()方法的时候会返回一个Student对象,现在要创建一个Student对象,代码如下:
Student s=new Student.Builder().name("CC").password("qwerty").sex("男").address("银河系第二旋臂").build();
建造者模式与抽象工厂模式的比较:
与抽象工厂模式相比,建造者模式返回一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族
在抽象工厂模式中,客户端实例化工厂类,然后调用工厂方法获取所需产品对象,而在建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整的对象
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车
说了这么多 Builder模式的好处,再来看看它有什么缺点:
Builder模式所创建的产品一般具有较多共同点,组成部分也相似,所以 Builder模式不适合创建差异性很大的产品类
产品内部变化复杂,会导致需要定义很多具体建造者类实现变化,增加项目中类的数量,增加系统的理解难度和运行成本
代理模式
代理模式是Java常见的设计模式之一。所谓代理模式是指客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。
为什么要采用这种间接的形式来调用对象呢?一般是因为客户端不想直接访问实际的对象,或者访问实际的对象存在困难,因此通过一个代理对象来完成间接的访问。
在现实生活中,这种情形非常的常见,比如请一个律师代理来打官司。
-
代理模式的实现
代理模式可以有两种实现的方式,一种是静态代理类,另一种是各大框架都喜欢的动态代理。下面我们主要讲解一下这两种代理模式 -
静态代理
Subject接口的实现
public interface Subject {
void visit();
}
实现了Subject接口的两个类:
public class RealSubject implements Subject {
private String name = "byhieg";
@Override
public void visit() {
System.out.println(name);
}
}
public class ProxySubject implements Subject{
private Subject subject;
public ProxySubject(Subject subject) {
this.subject = subject;
}
@Override
public void visit() {
subject.visit();
}
}
具体的调用如下:
public class Client {
public static void main(String[] args) {
// new RealSubject().visit();
ProxySubject subject = new ProxySubject(new RealSubject());
subject.visit();
}
}
通过上面的代理代码,我们可以看出代理模式的特点,代理类接受一个Subject接口的对象,任何实现该接口的对象,都可以通过代理类进行代理,增加了通用性。但是也有缺点,每一个代理类都必须实现一遍委托类(也就是realsubject)的接口,如果接口增加方法,则代理类也必须跟着修改。其次,代理类每一个接口对象对应一个委托对象,如果委托对象非常多,则静态代理类就非常臃肿,难以胜任。
- 动态代理
动态代理
动态代理有别于静态代理,是根据代理的对象,动态创建代理类。这样,就可以避免静态代理中代理类接口过多的问题。动态代理是实现方式,是通过反射来实现的,借助Java自带的java.lang.reflect.Proxy,通过固定的规则生成。
其步骤如下:
1、 编写一个委托类的接口,即静态代理的(Subject接口)
2、实现一个真正的委托类,即静态代理的(RealSubject类)
3、创建一个动态代理类,实现InvocationHandler接口,并重写该invoke方法
4、在测试类中,生成动态代理的对象。
第一二步骤,和静态代理一样,不过说了。第三步,代码如下:
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object, args);
return result;
}
}
第四步,创建动态代理的对象
Subject realSubject = new RealSubject();
DynamicProxy proxy = new DynamicProxy(realSubject);
ClassLoader classLoader = realSubject.getClass().getClassLoader();
Subject subject = (Subject) Proxy.newProxyInstance(classLoader, new Class[]{Subject.class}, proxy);
subject.visit();
创建动态代理的对象,需要借助Proxy.newProxyInstance。该方法的三个参数分别是:
ClassLoader loader表示当前使用到的appClassloader。
Class<?>[] interfaces表示目标对象实现的一组接口。
InvocationHandler h表示当前的InvocationHandler实现实例对象。
网友评论