面向对象设计原则、Java设计模式

面向对象设计原则、Java设计模式

  • 1.面向对象七大设计原则
    • 1. 开闭原则:对扩展开放,对修改关闭
    • 2. 依赖倒置原则:程序要依赖于抽象接口,不依赖与具体实现。
    • 3. 单一职责原则:一个类只做一件事,减少与类无关的功能
    • 4. 里式替换原则:多态
    • 5. 迪米特原则:减少对其他对象的了解
    • 6. 接口隔离原则:功能隔离,不把很多功能加载一个接口上
    • 7. 合成复用原则:优先使用组合
  • 2.Java 设计模式
    • 概述
    • 常用设计模式
      • 1.单例模式
        • 1.1饿汉式单例
        • 1.2懒汉式单例
          • 一般的加锁方式---效率低下
          • 双重检索 + volatile --- 效率提升
      • 2.工厂模式
        • 1.简单工厂模式
          • 使用简单工厂
        • 2抽象工厂模式
          • 使用抽象工厂
            • 1.创建多个产品接口及实现
            • 2. 创建抽象工厂类
            • 3. 创建产品对应的简单工厂
            • 4. 创建工厂生成类
            • 5.测试
      • 3.代理模式
        • 3.1静态代理
        • 3.2动态代理
          • 3.2.1jdk代理
          • 3.2.2Cglib代理
            • 总结:

1.面向对象七大设计原则

1. 开闭原则:对扩展开放,对修改关闭

将可能发生变化的类设计为抽象类,后续变化时,新增类来实现新的业务
可复用性和可维护性强,降低维护代码带来的新风险。

2. 依赖倒置原则:程序要依赖于抽象接口,不依赖与具体实现。

便于理解,提高了代码可读性

3. 单一职责原则:一个类只做一件事,减少与类无关的功能

如ArrayLsit和LinkedList,不同实现,不同功能。

4. 里式替换原则:多态

也就是多态,父类出现的地方都可以用子类替换
参数为List,传入ArrayList,LinkedList,Vector都可以
里式替换需要将父类设计为抽象类或接口,子类实现父类中所有方法

5. 迪米特原则:减少对其他对象的了解

对其他对象要最少了解。
不和陌生人说话,方法中创建的对象属于陌生人

6. 接口隔离原则:功能隔离,不把很多功能加载一个接口上

使用多个专门的接口,不要将多余功能加在一个接口上

在这里插入图片描述

7. 合成复用原则:优先使用组合

优先使用组合/聚合(has-a),其次考虑继承(只有is-a关系才使用继承)
组合:员工类包含部门,部门包含员工集合
继承:电脑 与 笔记本电脑,平板电脑,台式电脑等属于继承关系

2.Java 设计模式

概述

设计模式就是在长期的开发中,为了解决一些重复出现的问题,经过长期的总结优化,最终确定的一套解决方案。
优点:

  • 提高了代码的复用性、可维护性、可靠性、可读性、灵活性
  • 提高了程序员的思维能力、编码能力,设计能力,能够设计出标准化的程序

设计模式根据工作类型可划分为:创建型模式、结构性模式、行为型模式

  • 创建型模式
    如何创建对象?将对象创建与使用分离
    1. 单例 singleton:单例类只创建一个对象,所有线程共享
    2. 原型 prototype:每次新的请求都会对原型对象进行复制,得到新的对象
    3. 工厂方法 Factory Method:定义一个用于创建产品的接口,由子类决定生产什么产品
    4. 抽象工厂 AbstractFactory:提供一个创建产品族的接口,其每个 子类可以生产一系列相关的产品
    5. 建造者 Builder:将一个复杂对象分解成多个相对简单的部分,然
      后根据不同需要分别创建它们,最后构建成该复杂对象
  • 结构型模式
    类与类如何组合成更大的结构
    1. 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客 户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性
    2. 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口, 使得原本由于接口不兼容而不能一起工作的那些类能一起工作
    3. 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组 合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合 度
    4. 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能
    5. 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子 系统更加容易被访问
    6. 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复 用
    7. 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象 和组合对象具有一致的访问性
  • 行为型模式
    类和类之间如何协作、分工
    1. 模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定 义该算法的某些特定步骤
    2. 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
    3. 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开
    4. 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下 一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
    5. 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
    6. 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为
    7. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解
    8. 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数 据,而不暴露聚合对象的内部表示
    9. 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问
    10. 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它
    11. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的 解释方法,即解释器

常用设计模式

1.单例模式

设计一个类时,让他只能创建一个对象(节省资源,保证数据一致性)
单例模式需要实现的功能

  1. 单例类只能有一个对象
  2. 单例对象由单例类内部自己创建
  3. 单例类对外提供一个访问对象的方法

单例实现:

  • 构造方法私有化,在其他类中不能使用new创建对象
  • 向外界提供一个静态的方法,用来获取单例对象

1.1饿汉式单例

单例类一加载,就创建了对象,在调用getInstance方法之前就存在对象

public class HungrySingleton {
    private static HungrySingleton singleton = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return singleton;
    }
}

面向对象设计原则、Java设计模式_第1张图片

1.2懒汉式单例

懒汉式单例:类加载时,并不会去创建对象,只有第一次访问时,才去创建,存在线程安全问题
解决懒汉式单例线程安全:

  1. 加锁:效率低;只有第一次是创建对象,其他都获取对象
  2. volatile + 双重检索 :提高安全性、可靠性;提升了效率
一般的加锁方式—效率低下
public class LazySingleton {
    private static LazySingleton singleton = null;

    private LazySingleton() {}

    //方法加锁
    //效率低:创建对象只是第一次,后面都是获取对象。每次获取对象只能有一个线程获取,
    public static synchronized LazySingleton getInstance(){
        if(singleton == null){
            singleton = new LazySingleton();
        }
        return singleton;
    }

    //代码块加锁 : 效率也低
    public static LazySingleton getInstance() {
        synchronized (LazySingleton.class) {
            if (singleton == null) {
                singleton = new LazySingleton();
            }
        }
        return singleton;
    }
}

面向对象设计原则、Java设计模式_第2张图片

双重检索 + volatile — 效率提升

volatile
使用volatile修饰单例对象,主要是为了实现有序性
因为创建对象,给引用赋值的过程,可能被拆分为3个指令
①new Singleton():在内存中申请空间。(这条不会被重排)
②调用构造器初始化对象
③将对象地址赋给引用变量(有可能被重排到第二步执行,造成引用变量执行的是一个半成品对象)

双重检索

  • 多个线程第一次进到getInstance方法时,判断sington为null,其中一个线程获得锁,进入代码块中去创建对象,之后释放锁,其他进入第一次检索的线程也会去获取锁,拿到锁之后,要潘判断singleton是否为空,不为空说明已经有线程创建了对象,直接返回就行
  • 之后的线程访问getInstance方法时,第一层检索不为空直接返回单例对象

总结:

  • 第一层检索是检索单例对象是否被创建
  • 第二层检索是多线程第一次进入getInstance()时,判断是否有其他线程已经创建过对象了
package com.example.java_hign.framework.singleton;

public class LazySingleton {
	//volatile 为了保证有序性
    private static volatile LazySingleton singleton = null;
    
    private LazySingleton() {}
    
    //双重检索 + volatile
    public LazySingleton getInstance(){
        //多个线程第一次进入该方法,singleton 为 null
        if(singleton == null){
            //还没有初始化singleton时,只允许一个线程来创建
            synchronized (LazySingleton.class){
                //其他线程进来,如果singleton已经被初始化,就直接返回
                if(singleton == null){
                    singleton = new LazySingleton();
                }
            }
        }
        //后面线程获取对象时,直接返回创建好的singleton
        return singleton;
    }
}

2.工厂模式

工厂:批量创建对象 (属于设计模式中的创建型模式)
定义一个创建产品的工厂接口,具体的产品创建由接口的实现类来完成
并不对外显示创建对象的逻辑,只提供一个接口指向新创建的对象

思想:创建对象和使用对象相分离

什么时候使用工厂模式?
想要在不同的条件下创建不同的对象时

1.简单工厂模式

简单工厂模式中创建实例的方法是static修饰,简单工厂模式也叫静态工厂模式
何时使用简单工程模式?

  1. 需要创建的对象少,只需要一个工厂类就可以完成
  2. 客户端不需要关注对象的创建过程

优点
调用者想创建一个对象,只需要知道其名称即可

使用简单工厂

面向对象设计原则、Java设计模式_第3张图片

测试

public class Test {
	 public static void main(String[] args) {
		 SimpleFactory productFactory = new SimpleFactory();
		 Product phone =  productFactory.getProduct("com.example.java_hign.factory.Phone");
		 Product pad =  productFactory.getProduct("com.example.java_hign.factory.Pad");
		 Product computer =  productFactory.getProduct("com.example.java_hign.factory.Computer");
		 phone.displayInfo();
		 pad.displayInfo();
		 computer.displayInfo();
	 }
}

面向对象设计原则、Java设计模式_第4张图片

2抽象工厂模式

抽象工厂模式是用来创建工厂的(像超级管理员一样,可以创建不同的管理员,再由管理员去做其他事)

抽象工厂中

  • 接口用来创建一组相关对象的工厂
  • 每个生成的工厂可以按照简单工厂模式来生成对象
使用抽象工厂
1.创建多个产品接口及实现

阅读产品
面向对象设计原则、Java设计模式_第5张图片
电子产品
面向对象设计原则、Java设计模式_第6张图片

2. 创建抽象工厂类

面向对象设计原则、Java设计模式_第7张图片

3. 创建产品对应的简单工厂

阅读产品的工厂
面向对象设计原则、Java设计模式_第8张图片

电子产品的工厂
面向对象设计原则、Java设计模式_第9张图片

4. 创建工厂生成类

传入工厂的全类名,即可创建指定工厂
面向对象设计原则、Java设计模式_第10张图片

5.测试

5.1 使用抽象工厂生成电子产品工厂
使用电子产品工厂生成所属产品对象

public class Test {
    public static void main(String[] args) {
        //获取电子产品的简单工厂
        AbstractFactory factory = FactoryProducer.getFactory(
                "com.example.java_hign.factory.abstractfactory.ElectronicSimpleFactory");
        //获取电子产品工厂中的产品
        ElectronicProduct computer =  factory.getElectronicProduct(
                "com.example.java_hign.factory.abstractfactory.Computer");
        ElectronicProduct pad =  factory.getElectronicProduct(
                "com.example.java_hign.factory.abstractfactory.Pad");
        ElectronicProduct phone =  factory.getElectronicProduct(
                "com.example.java_hign.factory.abstractfactory.Phone");
        //测试产品
        computer.displayInfo();
        pad.displayInfo();
        phone.displayInfo();
    }
}

面向对象设计原则、Java设计模式_第11张图片
5.2 使用抽象工厂生成阅读产品工厂
使用阅读产品工厂生成所属产品对象

public class Test {
    public static void main(String[] args) {
        //获取阅读产品的简单工厂
        AbstractFactory factory = FactoryProducer.getFactory(
                "com.example.java_hign.factory.abstractfactory.ReadSimpleFactory");
        //获取阅读产品工厂中的产品
        ReadProduct book =  factory.getReadProduct(
                "com.example.java_hign.factory.abstractfactory.Book");
        ReadProduct ebook =  factory.getReadProduct(
                "com.example.java_hign.factory.abstractfactory.EBook");
        book.display();
        ebook.display();
    }
}

面向对象设计原则、Java设计模式_第12张图片

3.代理模式

当我们想完成某件事,但有不想直接访问目标时,可以通过代理来帮忙完成
(租房子,自己一个一个找房东(目标)效率太低,通过中间商来帮忙完成)
代理的优点:

  • 保护目标对象,不直接访问目标对象
  • 对访问者提供额外操作(代理可扩展目标对象的功能)
  • 降低了访问对象和目标对象的耦合度

3.1静态代理

实际中用的并不多
静态代理类必须实现和目标类一样的接口
扩展性很差
面向对象设计原则、Java设计模式_第13张图片
面向对象设计原则、Java设计模式_第14张图片

3.2动态代理

代理类不需要实现与目标类相同的接口,可以代理任意目标类
但要求目标类都实现接口
动态代理可以对目标类统一进行处理(添加日志)

3.2.1jdk代理

纯反射机制实现,动态获取目标类的方法
动态生成代理对象,目标类必须实现接口

步骤:

  1. 编写目标接口(UserDao)
  2. 编写目标类(UserDaoImpl)
  3. 编写动态代理类,实现InvocationHandler接口,重写invoke方法
  4. 测试类中生成代理类的对象
    面向对象设计原则、Java设计模式_第15张图片
    测试类
public class Test {
    public static void main(String[] args) {
        UserDao vipUser = new VipUserDaoImpl();
        InvocationHandler proxy = new DynamicProxy(vipUser);

        //真正的创建动态代理对象
        UserDao userDao = (UserDao) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(),VipUserDaoImpl.class.getInterfaces() , proxy);
        //调用的是invoke方法
        userDao.save(new User(20,"张飞"));
    }
}

在这里插入图片描述

3.2.2Cglib代理

目标类不需要实现接口,采用底层的动态字节码技术,为目标类动态生成子类,在子类中拦截父类方法的调用(采用方法拦截技术实现)。

使用了继承,不能代理final修饰的类

Cglib代理实现:

  1. 引入 cglib 的 jar 文件,spring核心包中集成了Cglib,引入spring-core-xxx.jar就可以
  2. 在内存中动态创建子类
  3. 不能代理final类,无法创建子类
  4. 无法拦截目标对象中的final 和 static 修饰的方法
总结:

静态代理是在代码中实现,代理类必须实现和目标类相同的接口,目标类接口修改或增加,代理类也要跟着改。

动态代理在运行时生成,代理类实现InvocationHandler接口,可以代理任意的目标类,要求目标类必须实现接口。

动态代理较于静态代理 : 减少了对接口的依赖,降低了耦合度。

你可能感兴趣的:(面试,java,设计模式,单例模式)