设计模式学习


参考文章:

Cyc2018-设计模式 六种简单的单例设计模式 JAVA设计模式之单例模式在开发中的最常用实现方式 简单工厂模式、工厂模式以及抽象工厂模式(具体) JAVA设计模式之抽象工厂模式 Java动态代理的两种实现方法 动态代理的两种方式以及区别

设计模式的作用:

需求经常变更,而设计模式的存在就是为了抵御需求变更。

1、单例模式

枚举单例模式如何防止反射攻击

单例模式存在的问题

1、初始化的线程安全问题 2、反序列化破坏单例 ObjectInputStream使用readObject的方法来实现对象的反序列化,readObject()方法会调用一个名为readOrdinaryObject的方法,该方法会通过反射的方式调用无参构造方法新建一个对象。 反序列化破坏单例
public class SerializableDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // Write to file
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
        oos.writeObject(Singleton.getSingleton());
        // Read from file
        File file = new File("tempFile");
        ObjectInputStream ois =  new ObjectInputStream(new FileInputStream(file));
        Singleton newInstance = (Singleton) ois.readObject();
        System.out.println(newInstance == Singleton.getSingleton());
    }
}
//false

解决办法:

1. 在单例类中增加一个 readResolve() 方法,方法中返回单例实例。因为 readOrdinaryObject() 方法在通过反射创建完对象后,会有一个条件判断,如果对象所在的类存在 readResolve() 方法,则会改为使用 readResolve() 方法获取实例对象,而不是利用反射创建出来的对象 参考:Java序列化破坏单例模式的解决方案N种(好文) 关于“如何破坏单例“我说了好几种方式,面试官却说:我其实随便问问,没想到你真会
import java.io.Serializable;
/**
     * 使用双重校验锁方式实现单例
     */
public class Singleton implements Serializable{
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
 
    private Object readResolve() {
        return singleton;
    }
}
  2. 使用枚举单例模式  反射在通过newInstance创建对象时,会检查该类是否ENUM修饰,如果是则抛出异常,反射失败。所以枚举类不能通过反射来创建对象
public enum  EnumSingleton {
    INSTANCE;
    public EnumSingleton getInstance(){
        return INSTANCE;
    }
}
参考:

简单工厂模式

在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。

实现过程:

把实例化的操作单独放到一个工厂类中,这个类有一个方法,这个方法会根据形参的值返回不同的子类实例。这个类就称为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。

好处:

这样做能把用户类和具体子类的实现解耦,用户类不再需要知道有哪些子类以及应当实例化哪个子类。而只需要向工厂类提供一些必要的信息,就能从工厂中取得它想要的类实例。用户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如某个子类的类名发生改变或者增加了某个子类,那么所有的客户类都要进行修改。如果使用了简单工厂,用户类不需要知道子类的细节,并且就算子类发生改变,也只要在工厂类的创建对象方法中增加一个分支判断就可以 了。

缺点:

每次增加子类或者删除子类对象都需要对简单工厂类进行修改。不符合开闭原则,所谓的开闭原则就是对扩展开放,对修改封闭,就是最好不要修改工厂的方法,而是采用功能的扩展的方式来达到目的。另外,随着子类数量的增加,简单工厂类会变得很庞大臃肿、耦合性高,所以就引入了工厂方法设计模式。

 

代码实现

interface Product{

}

class ConcreteProduct implements Product{

}

class ConcreteProduct1 implements Product{

}

class ConcreteProduct2 implements Product{

}
public class SimpleFactory {
    public Product createProduct(int type){
        if(type == 1){
            return new ConcreteProduct1();
        }else if(type == 2){
            return new ConcreteProduct2();
        }else{
            return new ConcreteProduct();
        }
    }
}
    public static void main(String[] args) {
        SimpleFactory simpleFactory = new SimpleFactory();
        Product product = simpleFactory.createProduct(2);
        // do something
    }
 

工厂方法模式

工厂方法模式是对简单工厂模式进一步的解耦,因为在工厂方法模式中是一个子类对应一个工厂类,而这些工厂类都实现于一个抽象接口。这相当于是把原本会因为业务代码而庞大的简单工厂类,拆分成了一个个的工厂类,这样代码就不会都耦合在同一个类里了。我们需要哪个类就创建哪个类的工厂,通过这个工厂来获取对象。

优点

1. 符合开闭原则,如果需要增加某个子产品,只需要增加一个创建该产品的产品工厂,这个产品工厂继承了抽象工厂,没有修改原有的代码。所以工厂方法模式克服了简单工厂会违背开-闭原则的缺点,又保持了封装对象创建过程的优点。 2. 工厂方法把实例化操作推迟到子类工厂,可以提高性能

缺点

随着产品数量的增加,具体产品工厂的会越来越多。

工厂方法模式代码:

interface Product{

}

class ConcreteProduct1 implements Product{

}

class ConcreteProduct2 implements Product{

}
abstract class Factory{
    public abstract Product createProduct();
}

class ConcreteFactory1 extends Factory{

    @Override
    public Product createProduct() {
        return new ConcreteProduct1();
    }
}

class ConcreteFactory2 extends Factory{
    @Override
    public Product createProduct() {
        return new ConcreteProduct2();
    }
}
public static void main(String[] args) {
        Factory concreteFactory1 = new ConcreteFactory1();
        Product product1 = concreteFactory1.createProduct();
        Factory concreteFactory2 = new ConcreteFactory2();
        Product product2 = concreteFactory2.createProduct();
    }

抽象工厂模式

抽象工厂模式是工厂方法模式的一个版本延伸,同样必须先有一个抽象工厂,这个工厂有很多子类产品工厂,但是这些产品工厂不再是只用来创建一个产品,而是用来创建一个配套系列的产品,创建一个产品系列家族。比如一个配套产品需要两个组件,组件1 和 组件 2, 如果我需要 A 套产品,我就只需要创建 A 工厂,A 工厂就可以为我提供 A1,  A2,两个组件, 如果我需要 B套产品,我就只需要创建 B工厂,B 工厂就可以为我提供 B1,  B2,两个组件,这样我们通过一个具体的工厂就可以获得一个产品链,一个配套系列的产品家族。 比如比如一个创建汽车的工厂,这个工厂有两个方法,一个方法用于创建空调对象,另一个方法用来创建一个没有空调的车对象。这样宝马工厂就用来创建宝马的空调,创建宝马的车体,奔驰工厂就用来创建适用于奔驰车的空调和奔驰车的车体。这样我们通过一个具体的工厂就可以获得一个产品链,一个配套系列的产品家族。

好处

 1. 抽象工厂模式最大的好处是易于交换产品系列,它只需要改变具体工厂即可使用不同的产品配置。不管是任何人的设计都无法去完全防止需求的更改,或者项目的维护,那么我们的理想便是让改动变得最小、最容易, 2. 抽象工厂模式的另一个好处就是它封装了具体的创建实例过程,使得我们不用去管这些创建实例的细节,简化了开发,减少了代码量

缺点

1. 每次获取实例对象都要创建工厂,如果用户程序非常多,就可能需要创建非常多的具体工厂。 2. 如果我们需要在一个系列中增加一个产品,那么需要在顶级工厂中增加一个方法,从而需要改动所有的具体工厂。

抽象工厂模式代码实现:

产品类:
abstract class AbstractProduct1{

}
abstract class AbstractProduct2{

}

class ProductA1 extends AbstractProduct1{


}

class ProductB1 extends AbstractProduct1{

}

class ProductA2 extends AbstractProduct2{


}

class ProductB2 extends AbstractProduct2{

}
工厂类:
abstract class Factory{
    abstract public AbstractProduct1 createProduct1();
    abstract public AbstractProduct2 createProduct2();
}

class ProductAFactory extends Factory{
    @Override
    public AbstractProduct1 createProduct1() {
        return new ProductA1();
    }

    @Override
    public AbstractProduct2 createProduct2() {
        return new ProductA2();
    }
}
class ProductBFactory extends Factory{
    @Override
    public AbstractProduct1 createProduct1() {
        return new ProductB1();
    }

    @Override
    public AbstractProduct2 createProduct2() {
        return new ProductB2();
    }
}
public static void main(String[] args) {
        // 创建一个家族A产品
        Factory factoryA = new ProductAFactory();
        AbstractProduct1 A1 = factoryA.createProduct1();
        AbstractProduct2 A2 = factoryA.createProduct2();
    }

动态代理设计模式

就是在目标类的基础上增加切面逻辑,生成增强的目标类 动态的代理的实现方式主要有两种,分别是 CGLib 动态代理和 JDK动态代理 JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。 CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

区别:

JDK代理只能对实现接口的类生成代理,没有实现接口的类不能被代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,因为通过继承类的实现方式,所以不能代理final修饰的类。 cglib通过修改字节码生成子类,创建效率较低,执行效率高;jdk动态代理的方式创建代理对象效率较高,但是因为通过反射机制代理对象,所以执行效率较低, jdk动态代理有一定的局限性,cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势

CGlib 代码实现

class Actor2{

    public void basicPerform(float money){
        System.out.println("拿到钱 " + money + ",开始基本表演");
    }

    public void dangerPerform(float money){
        System.out.println("拿到钱 " + money + ", 开始危险表演");
    }
}
public class CGLibProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
       String name = method.getName();
       Object rtValue = null;
       Float money = (Float)args[0];

       if("basicPerform".equals(name)){
           if(money > 2000){
               args[0] = money / 2;
               rtValue = methodProxy.invokeSuper(o, args);
           }
       }

       if("dangerPerform".equals(name)){
           if(money > 5000){
               args[0] = money / 2;
               rtValue = methodProxy.invokeSuper(o, args);
           }
       }
        return rtValue;
    }
}
class Client2{
    public static void main(String[] args) {
        CGLibProxy cglibProxy = new CGLibProxy();
        // 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Actor2.class);   // 设置Enhancer 的父类
        enhancer.setCallback(cglibProxy);

        // 创建代理对象
        Actor2  actor = (Actor2) enhancer.create();
        actor.basicPerform(3400);
        actor.dangerPerform(6500);
    }
}

CGLib 的匿名内部类实现

class Client2{
    public static void main(String[] args) {
        final Actor2 actor2 = new Actor2();
        // 创建代理对象
        Actor2 cglibActor = (Actor2)Enhancer.create(actor2.getClass(), new MethodInterceptor(){
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws InvocationTargetException, IllegalAccessException {
                String name = method.getName();
                Object rtValue = null;
                Float money = (Float)args[0];
                if("basicPerform".equals(name)){
                    // 基本演出
                    if(money > 2000){
                        rtValue = method.invoke(actor2, money / 2);
                    }
                }
                if("dangerPerform".equals(name)){
                    if(money > 5000){
                        rtValue = method.invoke(actor2, money / 2);
                    }
                }

                return rtValue;
            }
        });

        cglibActor.basicPerform(3000);
        cglibActor.dangerPerform(6500);
    }
}

jdk动态代理代码实现

interface IActor{
    // 基本表演
    public void basicPerform(float money);
    // 危险表演
    public void dangerPerform(float money);
}

class Actor implements IActor{
    @Override
    public void basicPerform(float money){
        System.out.println("拿到钱 " + money + ",开始基本表演");
    }
    @Override
    public void dangerPerform(float money){
        System.out.println("拿到钱 " + money + ", 开始危险表演");
    }
}
public class JDKProxy implements InvocationHandler{
    // 需要代理的对象
    public Object targetObject;
    /*public Object newProxy(Object targetObject){
        // 将目标对象传入进行代理
        this.targetObject = targetObject;
        return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(), this);
    }*/
    public JDKProxy(Object target){
        targetObject = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        Object rtValue = null;
        String name = method.getName();
        Float money = (Float)args[0];
        if("basicPerform".equals(name)){
            if(money > 2000){
                rtValue = method.invoke(targetObject, money / 2);
            }
        }
        if("dangerPerform".equals(name)){
            if(money > 5000){
                rtValue = method.invoke(targetObject, money / 2);
            }
        }
        return rtValue;
    }
}
class Client{
    public static void main(String[] args) {
        // 创建被代理对象
        IActor actor = new Actor();
        // 创建 Handler
        JDKProxy JDKInvocationHandler = new JDKProxy(actor);
        // 创建代理对象
        IActor actorProxy = (IActor) Proxy.newProxyInstance(actor.getClass().getClassLoader(),
                actor.getClass().getInterfaces(), JDKInvocationHandler);
        actorProxy.basicPerform(3000);
        actorProxy.dangerPerform(7000);
    }
}

JDK 动态代理的匿名内部类实现