设计模式

策略模式

定义一系列的算法,把每一个算法封装起来, 并且使它们可相互替换

环境类:操作策略的上下文环境,eg游客

抽象策略类:策略的抽象,eg出行方式的抽象

具体策略类:具体策略的实现,eg出行的具体实现

//抽象策略接口
interface TravelStrategy{
  public function travelAlgorithm();
}

//具体策略类
public class TrainStrategy implements TravelStrategy{
  @Override
  public void travelStyle(){
    System.out.println("TAKE THE TRAIN...");
  }
}
public class HighTrainStrategy implements TravelStrategy{
  @Override
  public void travelStyle(){
    System.out.println("TAKE THE HIGHTRAIN...");
  }
}
public class AirStrategy implements TravelStrategy{
  @Override
  public void travelStyle(){
    System.out.println("TAKE THE AIRPLANE...");
  }
}

//环境类
public class Traveler{
  TravelStrategy travelStrategy;
  //设置出行策略
  public void setTravelStrategy(TravelStrategy travelStrategy){
    this.travelStrategy = travelStrategy;
  }
  //为当前用户设置出行方式
  public void travelStyle(){
    travelStrateg.travelStyle();
  }
  public static void main(String[] args){
    Traveler traveler = new Traveler();
    traveler.setTravelStrategy(new TrainStrategy());
    traveler.travelStyle();
  }
}

策略模式的优点:

我们之前在选择出行方式的时候,往往会使用if-else语句,也就是用户不选择A那么就选择B这样的一种情况。这种情况耦合性太高了,而且代码臃肿,有了策略模式我们就可以避免这种现象,策略模式遵循开闭原则,实现代码的解耦合。扩展新的方法时也比较方便,只需要继承策略接口就好了

缺点:

客户端必须知道所有的策略类,并自行决定使用哪一个策略类。策略模式会出现很多的策略类。context在使用这些策略类的时候,这些策略类由于继承了策略接口,所以有些数据可能用不到,但是依然初始化了。

策略模式 VS 工厂模式

工厂模式是创建型模式 ,它关注对象创建,提供创建对象的接口,让对象的创建与具体的使用客户无关。 策略模式是对象行为型模式 ,它关注行为和算法的封装 。再举个例子,还是我们出去旅游,对于策略模式我们只需要选择其中一种出行方法就好,但是工厂模式不同,工厂模式是你决定哪种旅行方案后,由工厂代替你去构建具体方案(工厂代替你去买火车票)。

代理模式

提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.代理对象是对目标对象的扩展,并会调用目标对象

静态代理

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.

//接口
public interface IUserDao{
  void save();
}
//目标对象
public class UserDao implements IUserDao{
  public void save(){
    System.out.println("-----SAVE DATA!-----");
  }
}
//代理对象
public class UserDaoProxy implements IUserDao{
  private IUserDao target;
  public UserDaoProxy(IUserDao target){
    this.target = target;
  }
  public void save(){
    System.out.println("Start...");
    target.save();//执行目标对象的方法
    System.out.println("Commit...");
  }
}
//测试类
public class App{
  public static void main(String[] args){
    UserDao target = new UserDao();//目标对象
    UserDaoProxy proxy = new UserDaoProxy(target);//建立代理关系
    proxy.save();//执行代理方法
  }
}

总结:

1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.

2.缺点:

  • 因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
动态代理

代理对象不需要实现接口,动态代理AKA接口代理,JDK代理

动态代理解决的问题是面对新的需求时,不需要修改代理对象的代码,只需要新增接口和真实对象,在客户端调用即可完成新的代理。

目的:满足软件工程的开闭原则,提高类的可维护性和可扩展性。

JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

classLoader当前目标对象使用类加载器,interface目标对象实现的接口的类型,InvocationHandler事件处理,执

行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入

//接口
public interface IUserDao{
  void save();
}
//目标对象
public class UserDao implements IUserDao{
  public void save(){
    System.out.println("-----SAVE DATA!-----");
  }
}
//动态代理对象:代理工厂类
public class ProxyFactory{
  //保存接收目标对象
  private Object target;
  public ProxyFactory(Object target){
    this.target = target;
  }
  //给目标对象生成代理对象
  public Object getProxyInstance(){
    return Proxy.newProxyIntsance(target.getClass().getClassLoader(),
                                 target.getClass().getInterface(),
                                 new InvocationHandler(){
                                   @Override
                                   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
                                   {
                                     System.out.println("START2...");
                                     Object returnValue = methos.invoke(target, args);
                                     System.out.println("COMMIT2...");
                                     return returnValue;
                                   }                                 });
  }
}
//测试类
public class App{
  public static void main(String[] args){
    //目标对象
    IUserDao target = new UserDao();
    System.out.println(target.getClass());
    IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
    System.out.printl(proxy.getClass());
    proxy.save();
  }
}

总结:

代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

Cglib代理

有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

CGLIB可以代理没有实现接口的Java类

//目标对象
public class UserDao{
  public void save(){
    System.out.println("-----SAVE DATA!-----");
  }
}
//Cglib代理工厂
public class ProxyFactory implements MethodInterceptor{
  //维护目标对象
    private Object target;
    public ProxyFactory(Object target) {
        this.target = target;
    }
    //给目标对象创建一个代理对象
    public Object getProxyInstance(){
        //1.工具类
        Enhancer en = new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("开始事务...");
        //执行目标对象的方法
        Object returnValue = method.invoke(target, args);
        System.out.println("提交事务...");
        return returnValue;
    }
}
//测试类
public class App {
    public void test(){
        //目标对象
        UserDao target = new UserDao();
        //代理对象
        UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
        //执行代理对象的方法
        proxy.save();
    }
}

总结:必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法,因为采用的是继承,所以该类或方法最好不要生成final,对于final类或方法,是无法继承的

posted @ 2021-06-04 16:17  GladysChloe  阅读(37)  评论(0)    收藏  举报