这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/2021Softwarecodedevelopmenttechnology/
这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/2021Softwarecodedevelopmenttechnology/homework/11833
这个作业的目标 理解,熟悉设计原则,设计模式

参考:重学 Java 设计模式

六大原则

1. 开闭原则

开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。

2. 里氏代换原则

里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。
LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
如何理解基类可以出现的地方,子类一定可以出现 这句话呢?上面有说到在一个软件里,一个功能原先是由父类实现的,如果其子类在任何使用到这个父类的地方替换了父类,这个软件的功能不受影响,即遵循了里氏代换原则。

3. 单一职责原则

单一职责原则的定义很简单:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。
遵循单一职责原则的优点有:

  • 可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
  • 提高类的可读性,提高系统的可维护性;
  • 变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。

4. 依赖倒置原则

理解了面向接口编程,也就理解了依赖倒置原则
依赖倒置原则的定义是:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。
说的好像很抽象很复杂一样,但是简单来说依赖倒置原则的核心就是现在经常用上的面向接口编程。
依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。
在实际编程中,我们一般需要做到如下3点:

  • 低层模块尽量都要有抽象类或接口,或者两者都有。
  • 变量的声明类型尽量是抽象类或接口。
  • 使用继承时遵循里氏替换原则。

5. 接口隔离原则

接口隔离原则的定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上
接口隔离原则专注的是接口的设计,接口的设计应该尽量小,为每个类建立专用的接口,接口中的方法要尽量少。通过分散定义多个接口,可以预防外来变更的扩散,可以避免接口过于臃肿,减少不必要的实现代码,保证程序的灵活性,可维护性。
采用接口隔离原则对接口进行约束时,要注意以下几点:

  • 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
  • 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
  • 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情

6. 迪米特法则

迪米特法则,又称最少知道原则,其定义是:一个对象应该对其他对象保持最少的了解。
通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。
迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系。过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合。

创建型模式

工厂方法模式

工厂模式又称工厂方法模式,是一种创建型设计模式,其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
这种设计模式也是 Java 开发中最常见的一种模式,它的主要意图是定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
简单说就是为了提供代码结构的扩展性,屏蔽每一个功能类中的具体实现逻辑。让外部可以更加简单的只是知道调用即可,同时,这也是去掉众多 if-else 的方式。当然这可能也有一些缺点,比如需要实现的类非常多,如何去维护,怎样减低开发成本。

interface IProduct {
    public void productMethod();
}
 
class Product implements IProduct {
    public void productMethod() {
        System.out.println("产品");
    }
}
 
interface IFactory {
    public IProduct createProduct();
}
 
class Factory implements IFactory {
    public IProduct createProduct() {
        return new Product();
    }
}
 
public class Client {
    public static void main(String[] args) {
        IFactory factory = new Factory();
        IProduct prodect = factory.createProduct();
        prodect.productMethod();
    }
}

抽象工厂模式

抽象工厂模式与工厂方法模式虽然主要意图都是为了解决,接口选择问题。但在实现上,抽象工厂是一个中心工厂,创建其他工厂的模式。
抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。他与工厂方法模式的区别就在于,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构。我们依然拿生产汽车的例子来说明他们之间的区别。

优点:
抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。

缺点:
产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。
建造者模式

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

类图:
在上面的类图中有四个角色:

  • 产品类:一般是一个较为复杂的对象,也就是说构建对象的过程比较复杂。它是一个具体的类,可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。
  • 抽象建造者:引入抽象建造者的目的,是为了将建造的具体过程交与它的子类来实现。这样更容易扩展。一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品。
  • 建造者:具体的建造者类,主要封装了产品类的具体构建过程的复杂业务逻辑的代码。
  • 导演类:负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类。一般来说,导演类被用来封装程序中易变的部分。

优点:

  • 易于解耦:将产品本身和产品创建过程解耦,可以使用相同的过程得到不同的产品,也就是说细节依赖抽象。
  • 封装性好:使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在导演类中对整体而言可以取得比较好的稳定性。
  • 容易进行扩展:如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险

缺点:

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

应用场景:

  • 需要生成的产品对象有复杂的内部结构,这些产品对象具备共性;
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

原型模式

定义:用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。
原型模式主要用于对象的复制,它的核心是就是类图中的原型类 Prototype。Prototype 类需要具备以下两个条件:

  • 实现 Cloneable 接口。在 java 语言有一个 Cloneable 接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用 clone 方法。在 java 虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException 异常。
  • 重写 Object 类中的 clone 方法。Java 中,所有类的父类都是 Object 类,Object 类中有一个 clone 方法,作用是返回对象的一个拷贝,但是其作用域 protected 类型的,一般的类无法调用,因此,Prototype 类需要将clone 方法的作用域修改为 public 类型。
    原型模式是一种比较简单的模式,也非常容易理解,实现一个接口,重写一个方法即完成了原型模式。在实际应用中,原型模式很少单独出现。经常与其他模式混用,他的原型类 Prototype 也常用抽象类来替代。

优点:

  • 比直接使用 new 创建对象在性能上更好。因为 Object 类的 clone 方法是一个本地方法,直接操作内存中的二进制流,在复制大对象时性能差别明显
  • 简化对象的创建

使用场景:

  • 在需要重复地创建相似对象时可以考虑使用原型模式,可以提高性能

单例模式

定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式其实是很熟悉的一种设计模式了,只要是学过Spring的都知道,Spring在进行IoC容器初始化的时候,会自动帮我们创建一些实际并且帮我们管理起来,而这些实例Bean默认就是单例的。单例的意思就是在应用的生命周期里,该类的对象有且只有一个。
单例模式应该是23种设计模式中最简单的一种模式了。它有以下几个要素:

  • 私有的构造方法
  • 指向自己实例的私有静态引用
  • 以自己实例为返回值的静态的公有的方法。

实现单例模式有很多种方法,这里列举最常用的三种:

  1. 饿汉式单例:在单例类被加载时候,就实例化一个对象交给自己的引用
public class Singleton {
    private static Singleton singleton = new Singleton();
    
    private Singleton(){}
    
    public static Singleton getInstance(){
        return singleton;
    }
}
  1. 懒汉式单例:在调用取得实例方法的时候才会实例化对象
public class Singleton {
    private static Singleton singleton;
    
    private Singleton(){}
    
    public static synchronized Singleton getInstance(){
        if(singleton==null){
            singleton = new Singleton();
        }
        return singleton;
    }
}
  1. 双重校验锁单例
public class Singleton {
    private static volatile Singleton singleton;
    
    private Singleton(){}
    
    public static synchronized Singleton getInstance(){
        if(singleton==null){
            synchronized(this){
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

懒汉式单例有一个问题就是它是线程不安全的。但是我们可以通过加 synchronized 关键字修饰来加锁,这样虽然对性能有部分影响,但是保证了线程安全,并不会产生实例化多个对象的情况。

单例模式的优点:

  • 在内存中只有一个对象,节省内存空间。
  • 避免频繁的创建销毁对象,可以提高性能。
  • 避免对共享资源的多重占用。
  • 可以全局访问。

注意事项:

  • 只能使用单例类提供的方法得到单例对象,不要使用反射,否则将会实例化一个新对象。
  • 不要做断开单例类对象与类中静态引用的危险操作。
  • 多线程使用单例使用共享资源时,注意线程安全问题。

适用场景:

  • 需要频繁实例化然后销毁的对象。
  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
  • 有状态的工具类对象。
  • 频繁访问数据库或文件的对象。
  • 以及其他我没用过的所有要求只有一个对象的场景。
 posted on 2021-03-16 19:09  Chavy  阅读(86)  评论(0编辑  收藏  举报