设计模式学习笔记

设计模式学习笔记

1.概述
  • 设计模式的分类
范围\目的创建型模式结构型模式行为型模式
类模式工厂方法(类)适配器模板方法、解释器
对象 模式单例 原型 抽象工厂 建造者代理 (对象)适配器 桥接 装饰 外观 享元 组合策略 命令 职责链 状态 观察者 中介者 迭代器 访问者 备忘录
2.UML图
  • 类(Class)是指具有相同属性、方法和关系的对象的抽象,它封装了数据和行为,是面向对象程序设计(OOP)的基础,具有封装性、继承性和多态性等三大特性。在 UML 中,类使用包含类名、属性和操作且带有分隔线的矩形来表示。
    (1) 类名(Name)是一个字符串,例如,Student。

    [可见性]属性名:类型[=默认值]
    

    例如:-name:String
    注意:“可见性”表示该属性对类外的元素是否可见,包括公有(Public)、私有(Private)、受保护(Protected)和朋友(Friendly)4 种,在类图中分别用符号+、-、#、~表示。

    (3) 操作(Operations)是类的任意一个实例对象都可以使用的行为,是类的成员方法。UML 按以下格式表示:

    [可见性]名称(参数列表)[:返回类型]
    

    例如:+display():void。

    下图为学生类的描述:

在这里插入图片描述

  • 接口(Interface)是一种特殊的类,它具有类的结构但不可被实例化,只可以被子类实现。它包含抽象操作,但不包含属性。它描述了类或组件对外可见的动作。在 UML 中,接口使用一个带有名称的小圆圈来进行表示。如下图为接口的表示:

在这里插入图片描述

  • 类图(ClassDiagram)是用来显示系统中的类、接口、协作以及它们之间的静态结构和关系的一种静态模型。它主要用于描述软件系统的结构化设计,帮助人们简化对软件系统的理解,它是系统分析与设计阶段的重要产物,也是系统编码与测试的重要模型依据。

    类图中的类可以通过某种编程 语言直接实现。类图在软件系统开发的整个生命周期都是有效的,它是面向对象系统的建模中最常见的图。图 3 所示是“计算长方形和圆形的周长与面积”的类图,图形接口有计算面积和周长的抽象方法,长方形和圆形实现这两个方法供访问类调用。

在这里插入图片描述

  • 类之间的关系

    在软件系统中,类不是孤立存在的,类与类之间存在各种关系。根据类与类之间的耦合度从弱到强排列,UML 中的类图有以下几种关系:依赖关系、关联关系、聚合关系、组合关系、泛化关系和实现关系。其中泛化和实现的耦合度相等,它们是最强的。

    1. 依赖关系

    依赖(Dependency)关系是一种使用关系,它是对象之间耦合度最弱的一种关联方式,是临时性的关联。在代码中,某个类的方法通过局部变量、方法的参数或者对静态方法的调用来访问另一个类(被依赖类)中的某些方法来完成一些职责。
    在 UML 类图中,依赖关系使用带箭头的虚线来表示,箭头从使用类指向被依赖的类。图 4 所示是人与手机的关系图,人通过手机的语音传送方法打电话。
    在这里插入图片描述

    1. 关联关系

    关联(Association)关系是对象之间的一种引用关系,用于表示一类对象与另一类对象之间的联系,如老师和学生、师傅和徒弟、丈夫和妻子等。关联关系是类与类之间最常用的一种关系。关联可以是双向的,也可以是单向的。在 UML 类图中,双向的关联可以用带两个箭头或者没有箭头的实线来表示,单向的关联用带一个箭头的实线来表示,箭头从使用类指向被关联的类。也可以在关联线的两端标注角色名,代表两种不同的角色。

    在代码中通常将一个类的对象作为另一个类的成员变量来实现关联关系。图 5 所示是老师和学生的关系图,每个老师可以教多个学生,每个学生也可向多个老师学,他们是双向关联。

在这里插入图片描述

  1. 聚合关系

聚合(Aggregation)关系是关联关系的一种,是强关联关系,是整体和部分之间的关系,是 has-a 的关系。
聚合关系也是通过成员对象来实现的,其中成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在。例如,学校与老师的关系,学校包含老师,但如果学校停办了,老师依然存在。在 UML 类图中,聚合关系可以用带空心菱形的实线来表示,菱形指向整体。下图是大学和教师的关系图。

在这里插入图片描述

4.组合关系

组合(Composition)关系也是关联关系的一种,也表示类之间的整体与部分的关系,但它是一种更强烈的聚合关系,是 cxmtains-a 关系。
在组合关系中,整体对象可以控制部分对象的生命周期,一旦整体对象不存在,部分对象也将不存在,部分对象不能脱离整体对象而存在。例如,头和嘴的关系,没有了头,嘴也就不存在了。如下图为嘴和头的关系图

在这里插入图片描述

5.泛化关系

泛化(Generalization)关系是对象之间耦合度最大的一种关系,表示一般与特殊的关系,是父类与子类之间的关系,是一种继承关系,是 is-a 的关系。
在 UML 类图中,泛化关系用带空心三角箭头的实线来表示,箭头从子类指向父类。在代码实现时,使用面向对象的继承机制来实现泛化关系。例如,Student 类和 Teacher 类都是 Person 类的子类,其类图如下图 所示。

在这里插入图片描述
6.实现关系

实现(Realization)关系是接口与实现类之间的关系。在这种关系中,类实现了接口,类中的操作实现了接口中所声明的所有的抽象操作。
在 UML 类图中,实现关系使用带空心三角箭头的虚线来表示,箭头从实现类指向接口。例如,汽车和船实现了交通工具,其类图如下图 所示。

在这里插入图片描述

3.开闭原则
3.1 开闭原则定义
  • 软件实体应当对扩展开放,对修改关闭(Software entities should be open for extension,but closed for modification),这就是开闭原则的经典定义。
  • 这里的软件实体包括以下几个部分
    1. 项目中划分出的模块
    2. 类与接口
    3. 方法
  • 开闭原则的含义:当应用饿需求 改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求
3.2 开闭原则的作用
  • 对软件测试的影响

    软件遵守开闭原则的话,软件测试时只需要对扩展的代码进行测试就可以了,因为原有的测试代码仍能够正常运行

  • 可以提高代码的可复用性

    粒度越小,被复用的可能性就越大;在面向对象的程序设计中,根据原子和抽象编程可以提高代码的可复用性

  • 可以提高软件的可维护性

    遵守开闭原则的软件,其稳定性高和延续性强,从而易于扩展和维护。

3.3 开闭原则的实体方法
  • 可以通过"抽象约束、封装变化"来实现开闭原则,即通过接口或抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类进行扩展,当软件需求发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

  • 以Windows的桌面主体为例:

    windows的主题是桌面背景图片、窗口颜色和声音等元素的组合。用户可以根据自己的喜爱更换自己的桌面主题,也可以从网上下载新的主题。这些主题有共同的特点,可以为其定义一个抽象类,而每个具体的主体是其实现类。用户窗体可以根据需要选择或者增加新的主题,而不需要修改原代码,所以它是满足开闭原则的,如下图所示

在这里插入图片描述

4. 里式替换原则
4.1 里式替换原则定义
  • 继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects)。
  • 里式替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里式替换原则是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。
4.2 里式替换原则的作用
  • 里式替换原则是实现开闭原则的重要方式之一
  • 它克服了继承中重写父类造成的可复用性变差的缺点
  • 它是动作正确性的保证。即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性
4.3 里式替换原则的实现方法
  • 里式替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。也就是说:子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。

    如果通过重写父类的方法完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁的时候,程序运行出错的概率会非常大

    如果程序违反了里式替换原则,则继承类的对象在基类出现的方法会出现运行错误。这时其修正方法是:取消原来的继承关系,重新设计他们的关系。

  • 例子:关于里式替换原则的例子,最有名的是“正方形不是长方形”。当然,生活中也有很多例子,例如,企鹅、鸵鸟和几维鸟从生物学的角度来划分,它们属于鸟类;但从类的继承关系来看,由于它们不能继承鸟会飞的功能,所以它们不能定义成鸟的子类。同样,由于气球鱼不会游泳,所以不能定义成鱼的子类。

5. 依赖倒置原则
5.1 依赖倒置原则的定义
  • 依赖倒置原则原始定义为:高层模块不应该依赖底层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。即它的核心思想是:要面向接口编程,不要面向实现编程。
  • 依赖倒置原则是实现开闭原则的重要途径之一,它降低了客户与实现模块之间的耦合。
  • 在软件设计中,细节具有多边形,而抽象层则相对稳定,因此以抽象为基础搭建起来的架构要比以细节为基础搭建起来的架构要稳定得多。这里的抽象指的是接口或者抽象类,而细节是指具体的实现类。使用接口或抽象类的目的是指定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给它们的实现类去完成
5.2 依赖、倒置原则的作用
  • 降低类间的耦合性
  • 提高系统的稳定性
  • 减少并行开发引起的风险
  • 提高代码的可读性和可维护性
5.3 依赖倒置原则的实现方法
  • 依赖倒置原则的目的是通过面向接口的编程来降低类间的耦合性,所以我们在实际编程中只要遵循以下4点
    1. 每个类尽量提供接口或抽象类,或者两者都具备。
    2. 变量的声明类型尽量是接口或者是抽象类
    3. 任何类都不应该从具体类派生
    4. 使用继承时尽量遵循里式替换原则
6. 单一职责原则
6.1 单一职责原则的定义
  • 单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。
  • 该原则提出对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点:
    1. 一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力
    2. 当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。
6.2 单一职责原则的优点
  • 单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点
    1. 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多
    2. 提高类的可读性。复杂性降低,自然其可读性会提高
    3. 提高系统的可维护性。可读性提高,那自然更容易维护
    4. 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响
6.3 单一职责原则的实现方法
  • 单一职责是最简单但又最难运用的原则,需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中。

  • 大学学生工作管理程序为例

在这里插入图片描述

注意:单一职责同样也适用于方法。一个方法应该尽可能做好一件事,如果一个方法处理的事情太多,其粒度会变得很粗,不利于重用。

7. 接口隔离原则
7.1 接口隔离原则定义
  • 接口隔离原则要求程序员尽量将臃肿庞大的接口拆分成更小和更具体的接口,让接口中只包含客户感兴趣的方法。
  • 通俗来讲,就是要为各个类建立他们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用
  • 接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:
    1. 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离
    2. 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。
7.2 接口隔离原则的优点
  • 接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下五个优点
    1. 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性
    2. 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性
    3. 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险
    4. 使用多个专门的接口还能体现对象的层次,因为可以通过接口的继承,实现对总接口的定义
    5. 能减少项目工程中代码冗余。过大的接口里面通常放置许多不用的代码,当实现这个接口的时候,被迫设计冗余的代码
7.3 接口隔离的实现方法
  • 在具体应用接口隔离原则时,应该根据以下几个规则来衡量。

    1. 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑
    2. 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法
    3. 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同,深入了解业务逻辑
    4. 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情
  • 案例:学生成绩管理程序

    学生成绩管理程序一般包含插入成绩、删除成绩、修改成绩、计算总分、计算均分、打印成绩信息、査询成绩信息等功能,如果将这些功能全部放到一个接口中显然不太合理,正确的做法是将它们分别放在输入模块、统计模块和打印模块等 3 个模块中,其类图如图所示。

在这里插入图片描述

8. 迪米特法则
8.1 迪米特法则的定义
  • 如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。
8.2 迪米特法则的优点
  • 迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点

    1. 降低了类之间的耦合度,提高了模块的相对独立性
    2. 由于亲和度降低,从而提高了类的可复用率和系统饿扩展性

    但是过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在采用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

8.3 迪米特法则的实现方法
  • 从迪米特法则的定义和特点可知,它强调以下两点:

    1. 从依赖者的角度来说,只依赖应该依赖的对象
    2. 从被依赖着的角度来说,只暴露应该暴露的方法
  • 在运用迪米特法则时要注意以下6点。

    1. 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
    2. 在类的结构设计上,尽量降低类成员的访问权限
    3. 在类的设计上,优先考虑将一个类设置成不变类
    4. 在对其他类的引用上,将引用其他对象的次数降到最低
    5. 不暴露类的属性成员,而应该提高相应的访问器(setget方法)
    6. 谨慎使用序列化(Serializable)功能
  • 实例:明星与经纪人的关系

    明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如与粉丝的见面会,与媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则,其类图图所示。

在这里插入图片描述

9. 合成复用原则
9.1 合成复用原则的定义
  • 合成复用原则要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
  • 如果要使用继承关系,则必须严格遵循里式替换原则。合成复用原则同里式替换原则相辅相成,两者都是开闭原则的具体实现规范。
9.2 合成复用原则的重要性
  • 通常类的复用分为继承复用和合成复用两种,继承复用虽然有简单和易实现的优点,但它也存在以下缺点
    1. 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为 “白箱复用”
    2. 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。
    3. 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化
  • 采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点
    1. 它维护了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为"黑箱"复用
    2. 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口
    3. 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。
9.3 合成复用原则的实现方法
  • 合成复用原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。

  • 实例:汽车分类管理程序

    汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。如图 所示是用继淨:关系实现的汽车分类的类图。

    在这里插入图片描述

    如上图以看出用继承关系实现会产生很多子类,而且增加新的“动力源”或者增加新的“颜色”都要修改源代码,这违背了开闭原则,显然不可取。但如果改用组合关系实现就能很好地解决以上问题,其类图如下图所示。

在这里插入图片描述

10. 单例模式(Singleton)
10.1 单例模式的定义与特点
  • 单例模式的定义:指一个类只有一个实例,且该实例能自行创建这个实例的一种模式。例如,Windows中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示的内容不一致等错误
  • 在计算机系统中,还有Windows的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。
  • 单例有3个特点
    1. 单例类只有一个实例对象
    2. 该单例对象必须由单例类自行创建
    3. 单例类对外提供一个访问该单例的全局访问点
10.2 单例模式的结构与实现
  • 通常,普通类的构造函数是公有的,外部类可以通过"new 构造函数()"来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或或获取该静态私有实例

  • 单例模式的主要角色如下:

    1. 单例类:包含一个实例且能自行创建这个实例的类
    2. 访问类:使用单例的类

    其结构如图所示:

在这里插入图片描述

  • 单例模式的实现:

    1. 懒汉式单例

    该模式的特点是类加载时没有生成单例,只有当第一次调用getInstance方法时采取创建这个单例,代码为:

    public class LazySingleton
    {
        private static volatile LazySingleton instance=null;    //保证 instance 在所有线程中同步
        private LazySingleton(){}    //private 避免类在外部被实例化
        public static synchronized LazySingleton getInstance()
        {
            //getInstance 方法前加同步
            if(instance==null)
            {
                instance=new LazySingleton();
            }
            return instance;
        }
    }
    

    注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。

    1. 饿汉式单例

    该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。

    public class HungrySingleton
    {
        private static final HungrySingleton instance=new HungrySingleton();
        private HungrySingleton(){}
        public static HungrySingleton getInstance()
        {
            return instance;
        }
    }
    

​ 饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。

10.3 单例模式的应用场景
  • 在应用场景中,某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象的访问速度。如Web中的配置对象、数据库的连接池等
  • 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等
11. 原型模式(Prototype)
11.1 原型模式的定义与特点
  • 原型模式的定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。
11.2 原型模式的结构与实现
  • 原型模式包含以下主要角色:

    1. 抽象原型类:规定了具体原型对象必须实现的接口
    2. 具体原型类:实现抽象原型类的clone()方法,它是可被复制的对象
    3. 访问类:使用具体原型类中的clone()方法来复制新的对象

    其结构如图所示

在这里插入图片描述

  • 模式的实现

    原型模式的克隆分为浅克隆和深克隆,JAVA中的Object类提供了浅克隆的clone()方法,具体原型类只要实现Cloneable接口就可实现对象的浅克隆,

    代码如下:

    //具体原型类
    class Realizetype implements Cloneable
    {
        Realizetype()
        {
            System.out.println("具体原型创建成功!");
        }
        public Object clone() throws CloneNotSupportedException
        {
            System.out.println("具体原型复制成功!");
            return (Realizetype)super.clone();
        }
    }
    //原型模式的测试类
    public class PrototypeTest
    {
        public static void main(String[] args)throws CloneNotSupportedException
        {
            Realizetype obj1=new Realizetype();
            Realizetype obj2=(Realizetype)obj1.clone();
            System.out.println("obj1==obj2?"+(obj1==obj2));
        }
    }
    
11.3 原型模式的应用场景
  • 对象之间相同或相似,即知识个别的几个属性不同的时候
  • 对象的创建过程比较麻烦,但复制比较简单的时候
12. 工厂方法模式(FactoryMethod)
12.1 定义与特点
  • 工厂方法模式的定义:定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂当中。这满足创建型模式中所要求的的"创建与使用相分离"的特点

  • 我们把被创建的对象称为"产品",把创建产品的对象称为"工厂"。如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫"简单工厂模式",它并不属于GoF的23种经典设计模式,它的缺点是增加新产品时会违背"开闭原则"

  • 工厂方法模式的主要优点有:

    1. 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程
    2. 在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则;
  • 工厂模式的缺点:

    1.每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度。

12.2 模式的结构与实现
  • 工厂方法模式的主要角色有:

    1. 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法newProduct()来创建产品
    2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建
    3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特征和功能
    4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

    其结构图如图所示

在这里插入图片描述

  • 模式的实现
package FactoryMethod;
public class AbstractFactoryTest
{
    public static void main(String[] args)
    {
        try
        {
            Product a;
            AbstractFactory af;
            af=(AbstractFactory) ReadXML1.getObject();
            a=af.newProduct();
            a.show();
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
        }
    }
}
//抽象产品:提供了产品的接口
interface Product
{
    public void show();
}
//具体产品1:实现抽象产品中的抽象方法
class ConcreteProduct1 implements Product
{
    public void show()
    {
        System.out.println("具体产品1显示...");
    }
}
//具体产品2:实现抽象产品中的抽象方法
class ConcreteProduct2 implements Product
{
    public void show()
    {
        System.out.println("具体产品2显示...");
    }
}
//抽象工厂:提供了厂品的生成方法
interface AbstractFactory
{
    public Product newProduct();
}
//具体工厂1:实现了厂品的生成方法
class ConcreteFactory1 implements AbstractFactory
{
    public Product newProduct()
    {
        System.out.println("具体工厂1生成-->具体产品1...");
        return new ConcreteProduct1();
    }
}
//具体工厂2:实现了厂品的生成方法
class ConcreteFactory2 implements AbstractFactory
{
    public Product newProduct()
    {
        System.out.println("具体工厂2生成-->具体产品2...");
        return new ConcreteProduct2();
    }
}
package FactoryMethod;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;
class ReadXML1
{
    //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
    public static Object getObject()
    {
        try
        {
            //创建文档对象
            DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();
            DocumentBuilder builder=dFactory.newDocumentBuilder();
            Document doc;                           
            doc=builder.parse(new File("src/FactoryMethod/config1.xml"));        
            //获取包含类名的文本节点
            NodeList nl=doc.getElementsByTagName("className");
            Node classNode=nl.item(0).getFirstChild();
            String cName="FactoryMethod."+classNode.getNodeValue();
            //System.out.println("新类名:"+cName);
            //通过类名生成实例对象并将其返回
            Class<?> c=Class.forName(cName);
              Object obj=c.newInstance();
            return obj;
         }  
         catch(Exception e)
         {
                   e.printStackTrace();
                   return null;
         }
    }
}
12.3 工厂方法模式的应用场景
  • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
  • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
  • 客户不关心创建产品的细节,只关心产品的品牌。
13. 抽象工厂模式(AbstractFactory)
13.1 定义与特点
  • 工厂方法模式只考虑生产同等级的产品,如畜牧场只养动物、电视机厂只生产电视机、计算机软件学院只培养计算机软件专业的学生等。但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

  • 使用抽象工厂模式一般要满足以下条件。

    1. 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
    2. 系统一次只可能消费其中某一族产品,即同族的产品一起使用。
  • 抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。

    1. 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。

    2. 当增加一个新的产品族时不需要修改原代码,满足开闭原则。

  • 其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。

13.2 结构与实现
  • 抽象工厂模式的主要角色如下。

    1. 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
    2. 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
    3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
    4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。
  • 结构如图所示

在这里插入图片描述

  • 模式的实现

    1. 抽象工厂,提供了产品的生成方法
    interface AbstractFactory
    {
        public Product1 newProduct1();
        public Product2 newProduct2();
    }
    
    1. 具体工厂:实现了产品的生成方法。
    class ConcreteFactory1 implements AbstractFactory
    {
        public Product1 newProduct1()
        {
            System.out.println("具体工厂 1 生成-->具体产品 11...");
            return new ConcreteProduct11();
        }
        public Product2 newProduct2()
        {
            System.out.println("具体工厂 1 生成-->具体产品 21...");
            return new ConcreteProduct21();
        }
    }
    
13.3 应用场景
  • 抽象工厂模式通常适用于以下场景:
    1. 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
    2. 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
    3. 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值