设计模式-创建者模式

本文详细介绍了如何看懂UML类图,包括类图的定义、作用和表示方式。同时,文章深入探讨了类与类之间的各种关系,如关联、聚合、组合、依赖、继承和实现,以及这些关系的表示方法。此外,文章还提到了软件设计原则,如开闭原则、里氏替换原则、依赖倒置原则,强调了正确设计关系以提高代码的可扩展性和可维护性的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计模式
看懂UML类图和类与类之间的关系的表示方式
类图
  • 定义:类图(Class Diagram)是显示了模型的静态结构,特别是模型中存在的类、类的内部结构以及他们与其它类的关系等。类图不显示暂时性的信息、类图是面向对象建模的组成部分
  • 作用:软件工程中,类图是一种静态的结构图,描述了系统的类的集合、类的属性和类之间的关系,可以简化人们对系统的理解
  • 类图是系统分析和设计阶段的重要产物是系统编码和测试的重要模型
  • 表示方式:
    类图表示方式
    注意:括号中的内容表示是可选的,也有将类型放在变量名前面,返回值类型放在方法名前面的写法
类与类之间的关系的表示方式
* 设计模式六大原则:
* 1、单一职责原则,实现类要职责单一;
* 2、里氏替换原则,不要破坏继承体系;
* 3、依赖倒置原则,要面向接口编程;
* 4、接口隔离原则,在设计接口的时候要精简单一;
* 5、迪米特原则,要降低耦合;
* 6、开闭原则,要对扩展开放,对修改关闭。
* 7、合成复用原则:用聚合或者组合关系代替继承关系
  • 1.关联关系分为单向关联、双向关联、自关联

    • 关联关系是对象之间的一种引用关系,用于表示一类对象和另一类对象之间的联系,如老师和学生、师傅和徒弟、丈夫和妻子等。关联关系是类与类之间最常用的一种关系,分为一般关联关系、聚合关系和组合关系

    • 1.1 单向关联一个对象中持有另一个对象用一个带箭头的直线表示
      单项关联关系

    • 在UML图中单向关联关系用一个带箭头的实线表示。上图表示每个顾客都有一个地址,者通过让Customer类持有一个类型为Address的成员变量类实现

    • 1.2 双向关联两个对象之间各自持有对方类型的成员变量用一个不带箭头的直线表示
      双向关联关系上图中Customer类中维护一个List<Product>,表示一个顾客可以购买多个商品;在Product类中维护一个Customer类型的成员变量表示这个产品被哪个顾客所购买

    • 1.3 自关联用一个带有箭头且指向自身的线表示
      自关联上图的意思就是Node类包含类型为Node的成员变量,也就是“自己包含自己”

  • 2.聚合关系聚合关系是关联关系的一种,是强关联关系,是整体和部分之间的关系,UML中聚合关系可以用带空心菱形的实现来表示,菱形指向整体

    • 聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体而独立存在,例如:学校和老师的关系,学校包含老师,但是如果学校停办了,老师依然存在
      聚合关系
  • 3.组合关系组合表示类之间的整体与部分的关系,但它是一种更强的聚合关系

    • 组合关系用带实心菱形的实线来表示,菱形指向整体
    • 在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在了,部分对象也将不存在,部分对象不能脱离整体对象而存在。例如:头和嘴的关系,没有了头,嘴也就不存在了
      组合关系
  • 4.依赖关系:是一种使用关系,它是对象之间耦合度的最弱的一种关联关系,是临时性的关联。

    • 在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。
      依赖关系在UML图中,依赖关系使用带箭头的虚线来表示,箭头从实用类指向被依赖的类。上图是司机和汽车的关系图,司机驾驶汽车
  • 5.继承关系继承关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系

  • 在UML类图中,泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类。在代码实现时,使用面向对象的继承机制来实现泛化关系。例如Student类和Teacher类都是Person类的子类,其类图如下所示
    继承关系* 6.实现关系

  • 实现关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有抽象操作。

  • 在UML图中,实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。例如,汽车和船实现了交通工具
    实现关系

  • 总结:聚合、组合都是关联关系,聚合是强关联关系,组合是更强的聚合

  • 总结:依赖关系是对象之间耦合度最弱的一种关联关系

  • 总结:继承关系是对象之间耦合度最大的一种关系

软件设计原则
  • 1.开闭原则

    • 对扩展开放、对修改关闭
    • 在程序需要拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级
    • 想要达到这样的效果,我们需要使用接口和抽象类
    • 软件中易变的细节可以从抽象类派生来的实现类来进行扩展,当软件需要变化时,只需要根据需求重新派生一个实现类来扩展就可以了
  • 2.里氏替换原则是开闭原则的具体实现

    • 定义:子类可以扩展父类的功能,但不要改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,不要重写父类已经实现了的方法(抽象方法除外)。
    • 如果通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态
    • 任何基类可以出现的地方,子类一定可以出现 ,如果程序违背了里氏替换原则,则继承类的对象在基类出现的地方会出现运行错误。这时其修正方法是:取消原来的继承关系,重新设计它们之间的关系。
  • 3.依赖倒转原则是开闭原则的具体实现

    • 定义:高层模块不应该依赖低层模块,两者都应该依赖器抽象;抽象不应该依赖细节,细节应该依赖抽象简单的来说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合
    • 举例:比如电脑组装;一个电脑类中需要CPU类,内存条类,磁盘类等;如果我们直接在CPU类中将这三个类作为成员变量;那么就意味着这台电脑只能使用固定的品牌的CPU、内存条、磁盘组装;
    • 正确的做法应当是:提供CPU接口、内存条接口、磁盘接口;并将其作为电脑类的成员变量;这样电脑就可以替换不同种类的CPU、内存条、磁盘
  • 4.接口隔离原则

    • 一个类对另一个类的依赖应当建立在最小的接口上
    • 举例:门的功能,一个门可以具有防火、防盗、防水三种功能,但是并不是三种功能都必须具备。因此,我们在实现类的时候,不应当设计 门接口中具有防火、防盗、挡水三个抽象方法,因为这样我们在实现接口时,就必须实现所有的接口;
    • 正确的做法应当是:提供防火、防盗、防水三个接口,三个接口中分别提供防火、防盗、防水三个抽象方法;一个门类需要哪种功能就具体地实现哪个接口并重写其中的方法
  • 5.迪米特法则

    • 只和你的朋友交谈,不和陌生人交谈
    • 如果两个软件实体无需直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性
    • 迪米特法则中的**“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或者组合关系**
    • 如:明星类,粉丝类、公司类;他们之间本身没有什么关系;但是我们可以通过在经纪人类中把这三个类作为成员变量的方式,从而实现三个类之间的沟通
  • 6.合成复用原则

    • 定义:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
    • 通常的复用分为两种:
      • 1.继承复用,虽然简单、易于实现,但是有很多缺点

      • 缺点:

        • 1.继承复用破坏了类的封装性,因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。
        • 2.子类和父类的耦合度高,父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护
        • 3.它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时就已经定义,所以在运行时不可能发生变化
      • 2.采用组合或聚合复用 ,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能

      • 优点:

        • 1.它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用
        • 2.对象间的耦合度低,可以在类的成员位置声明抽象
        • 3.复用的灵活性高,这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象
      • 举例:汽车按“动力源”可以分为汽油汽车、电动汽车等;按“颜色”划分可以分为白色汽车、黑色汽车和红色汽车等,如果同时考虑这两种分类,其组合就很多,类图如下
        在这里插入图片描述 继承复用:
        继承复用

      • 从上面的类图我们可以看到使用继承复用产生了很多子类,如果现在又有新的动力源或者新的颜色的话,就需要再定义新的类,我们试着将继承复用改为聚合复用
        聚合复用

  • 补充:单一职责原则

  • 单一职责原则定义是:不要存在多于一个导致类变更的原因。通俗地说,即一个类只负责一项职责。

  • 单一职责原则针对的问题

    • 有一个类T负责两个不同的职责:职责P1和职责P2。当因为职责P1的需求发生改变而需要修改类T的时候,有可能会导致原本运行正常的职责P2功能发生故障。
  • 单一职责原则的解决方案

    • 遵循单一职责原则,分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1的时候,不会使职责P2发生故障风险。同理,当修改T2的时候,也不会使职责P1发生故障风险。
  • 单一职责原则的认识

    • 说到单一职责原则,很多人都会不屑一顾,因为它太简单了。稍有经验的程序员即使从来没有读过设计模式,从来没有听说过单一职责原则,在设计软件的时候也会自觉遵守这个重要原则,因为这是一个常识。在软件编程中,谁也不希望因为修改了一个功能导致其他的功能发生故障。而避免出现这一问题的方法便是遵循单一职责原则。虽然单一职责原则如此简单,并且被认为是常识,但是即使是经验丰富的程序员写出的程序也会有违背这一设计原则的代码存在。这是职责扩散导致的。所谓的职责扩散,指的就是因为某种原因,职责P1被分化为粒度更细的职责P1和P2。

    • 比如说,类T只负责一个职责P,这样设计是符合单一职责原则的。后来由于某种原因,或许是需求变更了,又或许是程序的设计者境界提高了,需要将职责P细分为粒度更细的职责P1和P2。这时候,如果要使程序遵循单一职责原则,就需要将类T也分解为两个类T1和T2,分别去负责P1和P2两个职责。但是在程序已经写好的情况下这样做,十分浪费时间和精力。所以,简单地修改类T,用它来负责两个职责是一个比较不错的选择,虽然这样做会有悖于单一职责原则。

    • 适当地违背单一职责原则有时候反而能提高开发效率,因此设计原则还是要按照实际需求来选择使用的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值