设计模式(初探工厂与代理模式)——学习记录

本文介绍了设计模式中的工厂模式和代理模式,详细阐述了这两种模式的概念、作用、优缺点及实现方式。工厂模式通过工厂方法创建对象,降低了系统的耦合度,提高了扩展性;代理模式则提供了间接访问目标对象的方式,方便扩展目标对象的功能。文章通过具体的代码示例展示了静态代理和JDK动态代理的应用,并对比了它们的优缺点。
摘要由CSDN通过智能技术生成

0 设计模式

  • 一种思维,一种态度,一种进步
1 创建型模式:省去new的过程
  1. 单例模式
  2. 工厂模式
  3. 抽象工厂模式
  4. 建造者模式
  5. 原型模式。
2 结构型模式:
  1. 适配器模式
  2. 桥接模式
  3. 装饰模式
  4. 组合模式
  5. 外观模式
  6. 享元模式
  7. 代理模式
3 行为型模式:
  1. 模板方法模式
  2. 命令模式
  3. 迭代器模式
  4. 观察者模式
  5. 中介者模式
  6. 备忘录模式
  7. 解释器模式
  8. 状态模式
  9. 策略模式
  10. 职责链模式
  11. 访问者模式。
2 介绍

经常看源码,分析源码使用哪种设计模式,大部分情况下,都是混合使用设计模式

  • 设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。

  • 使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性

  • 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。

  • 项目中合理地运用设计模式可以完美地解决很多问题,每种模式在现实中都有相应的原理来与之对应,每种模式都描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是设计模式能被广泛应用的原因。

3 面向对象设计==原则==。
  • 接口编程而不是对实现编程。
  • 优先使用对象组合而不是继承。
4 设计模式的六大原则
1、开闭原则(Open Close Principle)
  • 开闭原则的意思是:对扩展开放,对修改关闭
  • 在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。
  • 简言之,是为了使程序的扩展性好,易于 维护和升级
  • 想要达到这样的效果,我们需要使用接口抽象类,后面的具体设计中我们会提到这点。
2、里氏代换原则(Liskov Substitution Principle)
  • 里氏代换原则是面向对象设计的基本原则之一
  • 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。
  • LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为
  • 里氏代换原则是对开闭原则补充
  • 实现开闭原则的关键步骤就是抽象化,而基类与子类的 继承关系 就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
3、依赖倒转原则(Dependence Inversion Principle)

这个原则是开闭原则基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于 升级和维护 的软件设计思想,它强调降低依赖,降低耦合。

5、迪米特法则,又称最少知道原则(Demeter Principle)

最少知道原则是指:一个实体应当尽量****地与其他实体之间发生相互作用,使得系统功能模块相对独立

6、合成复用原则(Composite Reuse Principle)

合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承

1 工厂模式(Factory Pattern)

1.0 工厂模式研究
  • 作用:

    • 实现了创建者和调用者的分离
    • 详细分类:
      • 简单工厂模式
      • 工厂方法模式
      • 抽象工厂模式
  • OOP 七大原则:

    • 开闭原则:一个软件的实体应当对扩展开放,对修改关闭 (不修改原有的代码)
    • 依赖倒转原则:要针对接口编程,不要针对实现编程
    • 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信
  • 核心本质:

    • 实例化对象不使用new,用工厂方法代替
    • 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
  • 三种模式:

    • 简单工厂模式:不符合开闭原则

      • 用来生产同一等级结构中的任意产品(对于增加新的产品,必须扩展已有代码)

      • //车辆
        public interface Car {
        	void name();
        }
        public class Tesia implements Car {
        	@Override
        	public void name() {
        		// TODO Auto-generated method stub
        		System.out.println("特斯拉");
        	}
        }
        public class WuLing implements Car {
        	@Override
        	public void name() {
        		// TODO Auto-generated method stub
        		System.out.println("五菱宏光");
        	}
        }
        //新增车辆
        public class MoBai implements Car {
        	@Override
        	public void name() {
        		// TODO Auto-generated method stub
        		System.out.println("摩拜单车");
        	}
        }
        
      • //工厂
        //现在使用的是简单工厂模式,没有满足开闭原则:需要修改代码
        //开闭原则:对扩展开放,对修改关闭
        //如果再添加一辆车,这里不能修改,怎么办?——就需要工厂方法模式
        public class CarFactory {
            //方法一
        	public static Car getCar(String car) {
        		if (car.equals("五菱宏光")) {
        			return new WuLing();
        		}else if (car.equals("特斯拉")) {
        			return new Tesia();
        		}
        		return null;
        	}
            //方法二
            public static Car getWuLing(){
                return new WuLing();
            }
        }
        
      • //消费者
        public class Consumer {
        	public static void main(String[] args) {
        //		接口,所有的实现类!
        //		Car car=new WuLing();
        //		Car car2=new Tesia();
        		
        //		使用工厂创建,在这里不需要new,不需要关心是怎么new出来的,和里面的细节
        		//方法一
        		Car car= CarFactory.getCar("五菱宏光");
        		Car car2= CarFactory.getCar("特斯拉");
        		car.name();
        		car2.name();//五菱宏光,特斯拉
                //方法二
                Car wuLing = CarFactory.getWuLing();
        		wuLing.name();
        	}
        }
        
    • 工厂方法模式:符合开闭原则,但是工厂类越来越多,极其庞大

      • 用来生产同一等级结构中的固定产品(支持增加任意产品)

      • 车辆同上

      • //工厂方法模式
        public interface CarFactory {
        	Car getCar();
        }
        public class WulingFactory implements CarFactory {
        	@Override
        	public Car getCar() {
        		// TODO Auto-generated method stub
        		return new WuLing();
        	}
        }
        //新增车辆工厂实现类
        public class MoBaiFactory implements CarFactory {
        	@Override
        	public Car getCar() {
        		// TODO Auto-generated method stub
        		return new MoBai();
        	}
        }
        
      • //消费者
        public class Consumer {
        	public static void main(String[] args) {
        		Car car = new WulingFactory().getCar();
        		Car car2 = new TeslaFactory().getCar();
        		
        		car.name();
        		car2.name();
        		
        //		新增车辆:符合开闭原则,添加两个类
        		Car car3 = new MoBaiFactory().getCar();
        		car3.name();
        	}
        }
        
    • 以上两种工厂模式哪个比较

      • 结构复杂度:简单工厂模式
      • 代码复杂度:简单工厂模式
      • 编程复杂度:简单工厂模式
      • 管理上的复杂度:简单工厂模式
      • 根据设计原则:工厂方法模式
      • 根据实际业务:简单工厂模式
    • 抽象工厂模式

      • 围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂

      • 涉及到两个维度

  • 小结:

    • 简单工厂模式(静态工厂模式)
      • 虽然某种程度上不符合设计原则,但实际使用最多!
    • 工厂方法模式
      • 不修改已有类的前提下,通过增加新的工厂类实现扩展
    • 抽象工厂模式
      • 不可以增加产品(只负责工厂),可以增加产品族!
    • 应用场景:
      1. JDK中Calendar的getInstance方法
      2. JDBC中的Connection对象的获取
      3. Spring中IOC容器创建管理bean对象
      4. 反射中Class对象的newlnstance方法
1.1 介绍
  • 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一, 工厂模式是我们最常用的实例化对象模式了。
  • 这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  • 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
  • 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
  • 著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。
  • 因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,
  • 如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑使用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。
  • 本质: 工厂方法代替new操作

应用实例:

  1. 您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。

  2. Hibernate 换数据库只需换方言和驱动就可以。

优点:

  1. 一个调用者想创建一个对象,只要知道其名称就可以了。

  2. 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。

  3. 屏蔽产品的具体实现,调用者只关心产品的接口。

**缺点:**每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。

使用场景:

  1. 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
  2. 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
  3. 设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。

**注意事项:**作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。

1.2 实现

我们将创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory

FactoryPatternDemo,我们的演示类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JroSfdp8-1650195750385)(%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F.assets/factory_pattern_uml_diagram.jpg)]

2 代理模式 (Proxy Pattern)

  • 代理(Proxy)是一种设计模式,提供了 间接 对目标对象进行 访问 的方式;

  • 即通过代理对象访问目标对象.

  • 这样做的好处是:可以在目标对象实现的功能上,增加额外的功能补充,即扩展目标对象的功能.

  • 这就符合了设计模式的开闭原则,即在对既有代码不改动的情况下进行功能的扩展。

  • 举个例子来说明代理的作用:明星与经纪人之间就是被代理和代理的关系,明星出演活动的时候,明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)

  • 来解决.这就是代理思想在现实中的一个例子。

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

  • 代码案例:

// 接口
interface IStar {
    void sing();
}
// 真实对象
class LDHStar implements IStar {
    @Override
    public void sing() {
        System.out.println("刘德华唱歌");
    }
}
// 代理类需要有真实对象的控制权 (引用)
class ProxyManger implements IStar {        
    // 真实对象的引用
    private IStar star;        
    public ProxyManger() {
        super();
    }
    public ProxyManger(IStar star) {
        super();
        this.star = star;
    }

    @Override
    public void sing() {      
        System.out.println("唱歌前准备");        
        star.sing();       
        System.out.println("善后工作");        
    }
}
class Test{
    public static void main(String[] args) {
        // 创建明星对象
        IStar ldh = new LDHStar();
        ProxyManger proxy = new ProxyManger(ldh);
        proxy.sing();
    }
}
静态代理总结
  • 优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展.

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

  • 而动态代理方式可以解决上面的问题

2.2 动态代理
  • 动态代理的主要特点就是能够在程序运行时 JVM 才为目标对象生成代理对象。

  • 常说的动态代理也叫做 JDK代理 也是一种 接口代理

  • JDK中生成代理对象的代理类就是Proxy,所在包是java.lang.reflect*
    *

//目标类接口
interface IDog{
    void run();               
}
//目标类
class GunDog implements IDog{
    @Override
    public void run() {
        System.out.println("猎狗在跑");
    }
}
class DogUtils{
    public static void method1() {
        System.out.println("增强方式一");
    }
    
    public static void method2() {
        System.out.println("增强方式二");
    }
}
class MyInvocationHandle implements InvocationHandler{
    private Object target;
    public void setTarget(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
    throws Throwable {
        DogUtils.method1();
        method.invoke(target, args);
        DogUtils.method2();
        return null;
    }
}
//生产代理对象的工厂
class MyProxyFactory{
    public static Object getProxy(Object target) {
        MyInvocationHandle handle = new MyInvocationHandle();
        handle.setTarget(target);
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), 
            target.getClass().getInterfaces(), handle);
        return proxy;
	}
}
public class ProxyDemo {
    public static void main(String[] args) {
      IDog dog = new GunDog();
      IDog proxy =(IDog) MyProxyFactory.getProxy(dog);
      proxy.run();
    }
}
总结
  • 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能使用动态代理,因此这也算是这种方式的缺陷。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值