简介:设计模式是软件工程中的最佳实践,它们为解决常见的设计问题提供可重用的解决方案。本压缩包提供23种经典设计模式的C#实现,并探讨其在实际开发中的应用。学习这些设计模式有助于开发者编写更高效、可维护的代码,并在实际项目中通过灵活应用多种模式提升代码质量和可维护性。 
1. 软件工程中的设计模式概述
在软件工程的广阔领域中,设计模式是一组被广泛认可的解决方案,用于解决软件开发过程中常见问题的模板和指导方针。它们不仅提高了代码的可读性和可维护性,而且促进了团队之间的沟通,因为设计模式使得开发团队能够明确地传达其设计意图。
设计模式可以分为三大类:创建型、结构型和行为型,它们分别关注于对象创建、对象间的关系以及算法和对象间职责的分配。
1.1 设计模式的重要性
设计模式并非要编写千篇一律的代码,而是提供了一种通用语言,帮助开发者表达设计理念。它们是经验丰富的开发人员在面对特定问题时的智慧结晶,因此,熟练掌握并合理应用设计模式,对于提高软件开发效率和质量至关重要。
1.2 设计模式的起源与发展
设计模式的概念起源于建筑领域,由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides这四位软件工程师编写的《Design Patterns: Elements of Reusable Object-Oriented Software》一书正式将其引入软件工程领域。此书通常被称为“四人帮”(Gang of Four,简称GoF),是学习设计模式不可或缺的经典之作。
1.3 如何学习和应用设计模式
学习设计模式应从理解它们解决的问题开始,然后研究每种模式的结构和实现方式。实践是学习设计模式的关键,通过编写代码来模拟真实世界中的案例,可以加深对设计模式的理解并掌握其应用场景。
设计模式是软件开发的基石,它们为开发者提供了一套既定的框架,帮助构建灵活且可维护的系统。在接下来的章节中,我们将深入了解并探讨不同类型的设计模式,并通过实际例子来展示如何在实际项目中应用它们。
2. 单例模式及线程安全
2.1 单例模式的理解与实现
2.1.1 单例模式的基本概念
单例模式是设计模式中最简单但也是最常用的模式之一。它的核心是保证一个类仅有一个实例,并提供一个全局访问点。这种模式确保了对象的唯一性,避免了重复创建对象所造成的资源浪费。在软件开发过程中,单例模式常用于管理那些对系统全局只有一个实例的资源,比如线程池、缓存、对话框以及日志对象等。
2.1.2 实现单例的关键技术
要实现单例模式,关键在于控制对象的创建过程,并确保全局只有一个访问点。常见的实现方式包括懒汉式和饿汉式。
懒汉式
懒汉式单例模式是指直到第一次访问时,才创建单例对象。这种方式的优点是节省了内存,缺点是在多线程环境下可能会出现多个实例的情况。
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉式
饿汉式单例模式则是在类加载时就完成了初始化,这种方法天生是线程安全的,但是可能会导致资源的浪费。
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
2.2 线程安全与单例模式
2.2.1 线程安全的必要性
在多线程环境下,单例模式的实现需要考虑线程安全的问题。如果多个线程同时访问单例类的创建方法,而该方法不是线程安全的,那么可能会创建多个实例,这违背了单例模式的原则。因此,确保单例模式的线程安全是至关重要的。
2.2.2 单例模式的线程安全实现
为了保证单例模式的线程安全,可以采用不同的策略。常见的线程安全实现有双重检测锁定和静态内部类。
双重检测锁定
双重检测锁定模式是在懒汉式单例模式的基础上增加了一个检查,确保只在对象未被创建时才进行同步,从而减少不必要的同步开销。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类
静态内部类单例模式利用了Java类加载机制,通过内部类延迟加载实例,保证了线程安全。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
通过以上几种方式,我们可以在保证单例模式的基本原则的同时,实现线程安全的单例模式。接下来,我们将探讨工厂模式与对象创建封装的相关内容。
3. 工厂模式与对象创建封装
在软件工程中,创建型设计模式的主要目的是构建对象的同时,隐藏创建逻辑,而不是使用new直接实例化一个对象。这样的设计可以提高代码的灵活性和可复用性。工厂模式作为一种创建型设计模式,它提供了一种创建对象的最佳方式,它关注于创建对象,而不用关心这些对象的类和如何创建这些对象的细节。
工厂模式可以分为三种基本类型:简单工厂模式、工厂方法模式和抽象工厂模式。每种模式都有其特定的应用场景和实现方式。下面将逐一详细介绍这些模式,并通过代码示例和逻辑分析来展示其背后的工作机制。
3.1 简单工厂模式
3.1.1 简单工厂模式的定义
简单工厂模式是一种创建型设计模式,提供了一个创建对象的类,但不让其他类来决定要创建的对象类。简单工厂允许用户通过传递参数来决定创建哪一种类的实例。简单工厂模式在创建对象时,根据不同的输入参数,返回不同类的实例。
3.1.2 简单工厂模式的应用
简单工厂模式的一个典型应用场景是创建产品族中的具体产品,它能够在客户端不知道具体产品类的情况下,通过传入参数的方式生成产品实例。
实例代码分析:
// 假设有两种产品,ProductA和ProductB
class ProductA { }
class ProductB { }
// 简单工厂类
class SimpleFactory {
public static ProductA createProductA() {
return new ProductA();
}
public static ProductB createProductB() {
return new ProductB();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 创建ProductA的实例
ProductA productA = SimpleFactory.createProductA();
// 创建ProductB的实例
ProductB productB = SimpleFactory.createProductB();
// 客户端代码不需要知道ProductA和ProductB的具体实现细节
}
}
在上述代码中, SimpleFactory 类使用 static 方法创建了 ProductA 和 ProductB 的实例。客户端代码通过调用这些静态方法而不是直接使用 new 关键字来创建对象。这样做的好处是,当创建逻辑发生变化时,我们只需要修改 SimpleFactory 类中的实现即可,而不需要改变客户端代码。
3.2 工厂方法模式
3.2.1 工厂方法模式的原理
工厂方法模式是简单工厂模式的进一步抽象和扩展。工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法把实例化操作推迟到子类中完成,这样可以适应需求的变化。
3.2.2 工厂方法模式的实例分析
假设我们有一个生产汽车的工厂,这个工厂可以生产多种类型的汽车。工厂方法模式允许我们为每种类型的汽车创建不同的工厂类。
// 汽车接口
interface Car {
void drive();
}
// 具体汽车类
class BMWCar implements Car {
public void drive() {
System.out.println("Driving BMW car");
}
}
class BenzCar implements Car {
public void drive() {
System.out.println("Driving Benz car");
}
}
// 汽车工厂接口
interface CarFactory {
Car produceCar();
}
// 具体工厂类
class BMWFactory implements CarFactory {
public Car produceCar() {
return new BMWCar();
}
}
class BenzFactory implements CarFactory {
public Car produceCar() {
return new BenzCar();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
CarFactory carFactory = new BMWFactory();
Car car = carFactory.produceCar();
car.drive();
carFactory = new BenzFactory();
car = carFactory.produceCar();
car.drive();
}
}
在上述代码中, Car 接口定义了一个 drive 方法, BMWCar 和 BenzCar 分别实现了 Car 接口。 CarFactory 定义了 produceCar 方法用于生产 Car 对象。然后我们有 BMWFactory 和 BenzFactory ,它们都是 CarFactory 的实现,分别用于生产 BMWCar 和 BenzCar 对象。
3.3 抽象工厂模式
3.3.1 抽象工厂模式的特点
抽象工厂模式提供了一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。它是一种创建型模式,更关注于一组对象而不是单个对象的创建。抽象工厂可以创建一系列相关的产品,而无需指定这些产品具体的类。
3.3.2 抽象工厂模式的高级应用
假设我们有一个家居制造公司,它可以生产各种类型的家具产品。使用抽象工厂模式,我们可以创建一个工厂来生产一系列家具产品,比如沙发、椅子和桌子。
// 沙发接口
interface Sofa {
void sit();
}
// 椅子接口
interface Chair {
void sit();
}
// 桌子接口
interface Table {
void placeItems();
}
// 具体产品类
class LeatherSofa implements Sofa {
public void sit() {
System.out.println("Sitting on a leather sofa");
}
}
class WoodenChair implements Chair {
public void sit() {
System.out.println("Sitting on a wooden chair");
}
}
class GlassTable implements Table {
public void placeItems() {
System.out.println("Items placed on a glass table");
}
}
// 抽象工厂接口
interface FurnitureFactory {
Sofa produceSofa();
Chair produceChair();
Table produceTable();
}
// 具体工厂类
class LuxuryFurnitureFactory implements FurnitureFactory {
public Sofa produceSofa() {
return new LeatherSofa();
}
public Chair produceChair() {
return new WoodenChair();
}
public Table produceTable() {
return new GlassTable();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
FurnitureFactory furnitureFactory = new LuxuryFurnitureFactory();
Sofa sofa = furnitureFactory.produceSofa();
Chair chair = furnitureFactory.produceChair();
Table table = furnitureFactory.produceTable();
sofa.sit();
chair.sit();
table.placeItems();
}
}
在上述代码中,我们定义了三个产品接口 Sofa 、 Chair 和 Table ,以及它们各自的实现。抽象工厂接口 FurnitureFactory 定义了生产这些产品的方法。 LuxuryFurnitureFactory 实现了 FurnitureFactory 接口,用于生产豪华风格的家具产品。
以上实例展示了简单工厂、工厂方法和抽象工厂模式的基本原理和应用。每种模式都提供了不同程度的抽象和灵活性,在不同的需求场景下,我们可以选择合适的工厂模式来使用。简单工厂模式适合产品种类较少时使用,工厂方法模式适合产品种类固定且扩展性要求不高的情况,而抽象工厂模式适合产品系列化、产品族业务逻辑较为复杂的场景。
4. 构造函数模式与对象构造
4.1 构造函数模式的原理
4.1.1 构造函数模式的定义与作用
构造函数模式是JavaScript中创建和初始化对象的一种基本技术。构造函数是一种特殊类型的函数,它主要用来初始化新创建的对象。构造函数通常被命名为大写字母开头,以区别于普通函数,并且在构造函数内部使用 this 关键字来指代新创建的对象。
在使用构造函数模式时,我们通过 new 关键字来调用构造函数,从而创建一个新对象。当 new 操作符被用于一个构造函数时,会发生以下四件事:
- 创建一个全新的空对象;
- 将构造函数的作用域赋给新对象(因此
this指向新对象); - 执行构造函数内的代码(为新对象添加属性);
- 返回新对象。
这种模式的优势在于它提供了一种方便的方式来创建具有特定初始状态的对象。通过构造函数,可以定义对象的初始值,并在创建对象时直接设置它们。
4.1.2 构造函数模式的优势与缺陷
优势: 1. 代码复用: 构造函数模式允许我们在创建多个对象时重用同一个构造函数。 2. 清晰的构造: 每个对象的创建都遵循相同的初始化过程,确保了对象创建的一致性和可预测性。 3. 直观的接口: 通过构造函数模式,代码的可读性增强了,开发者能直观地看到对象是如何被创建和初始化的。
缺陷: 1. 属性值不能共享: 每个通过构造函数创建的对象都有自己的属性副本,导致内存中存在重复的数据,这在某些情况下可能造成资源浪费。 2. 方法复用性差: 构造函数中的方法会在每个对象实例中创建一个副本,而不是像原型中那样共享同一个方法。
4.1.3 代码块:构造函数模式的实现
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
Person.prototype.greet = function() {
return `Hello, my name is ${this.firstName} ${this.lastName}`;
};
var john = new Person('John', 'Doe');
console.log(john.greet()); // 输出:Hello, my name is John Doe
在上面的代码中,我们定义了一个 Person 构造函数,它接受两个参数 firstName 和 lastName 。通过 new Person() 创建的新对象会有 firstName 和 lastName 属性。我们还可以通过 Person.prototype 给构造函数添加方法,这样所有通过 Person 构造函数创建的对象都可以共享这个方法。
4.1.4 构造函数模式的进一步探讨
使用构造函数模式时,我们应注意它不会创建一个真正的原型链关系。每个实例的 prototype 属性都是 Object 的默认原型,因此它们不是彼此的原型。如果想要多个对象共享相同的方法,应考虑使用原型链或ES6引入的类继承机制。
4.2 对象构造的最佳实践
4.2.1 有效利用构造函数
为了充分利用构造函数模式,开发者可以遵循以下实践:
- 标准化参数: 通过构造函数标准化对象的初始化过程,可以避免在对象上手动赋值属性,从而减少错误和提高代码的清晰度。
- 单一职责: 每个构造函数应该负责创建一种类型的对象,并且完成该对象的基本初始化工作。这样可以保持代码的模块化和可维护性。
- 避免在构造函数中返回值: 在构造函数中返回值往往会覆盖
this,导致构造函数返回undefined。正确的做法是让构造函数内部完成所有的初始化,然后返回新创建的对象实例。
4.2.2 设计模式在对象构造中的应用案例
在某些情况下,构造函数模式可能不足以解决复杂问题。例如,当我们需要动态地构造对象,或者需要在不同类型的对象间共享功能时,可能需要借助其他设计模式。以下是一个案例,展示了如何结合工厂模式和构造函数模式来创建对象。
// 使用工厂模式来创建对象
function createUserAccount(type) {
if (type === 'admin') {
return new AdminAccount();
} else {
return new StandardAccount();
}
}
// 构造函数用于创建管理员账户
function AdminAccount() {
this.role = 'admin';
this的权利 = ['createUsers', 'deleteUsers'];
}
// 构造函数用于创建标准用户账户
function StandardAccount() {
this.role = 'standard';
this的权利 = ['accessSystem'];
}
// 使用工厂函数创建一个管理员账户
var admin = createUserAccount('admin');
console.log(admin.role); // 输出:admin
console.log(admin的权利); // 输出:['createUsers', 'deleteUsers']
在这个案例中,我们定义了一个 createUserAccount 工厂函数,它根据提供的类型参数来创建不同类型的用户账户对象。 AdminAccount 和 StandardAccount 作为构造函数,为每种账户类型定义了特定的属性。这种组合方式使得代码更加模块化,易于维护和扩展。
4.2.3 对象构造的扩展性分析
为了提高代码的扩展性,开发者应该:
- 使用原型继承: 当需要为多个对象添加通用方法时,可以通过原型继承来实现。这样,添加到原型的方法将被所有实例共享。
- 考虑组合而非继承: 对于更复杂的场景,可以考虑使用组合模式而非传统的继承。组合允许对象在运行时动态地组合其他对象的功能,提供更高的灵活性。
- 利用模块化和命名空间: 如果应用中有大量的构造函数和方法,应该考虑使用模块化和命名空间来组织代码,这样可以避免全局作用域的污染,同时提高代码的可读性和可维护性。
通过遵循这些最佳实践,开发者可以更有效地使用构造函数模式,从而构建出结构清晰、易于维护的JavaScript应用程序。
5. 原型模式与性能优化
5.1 原型模式的实现机制
5.1.1 原型模式的定义与应用范围
原型模式是一种创建型设计模式,它用于创建重复的对象,同时又能保证性能。通过这种方式,可以不必再执行显式的初始化代码,从而节省资源。原型模式利用了对象的克隆技术,即通过已有对象快速复制出新的对象实例。
该模式广泛应用于: - 避免重复的初始化操作,特别是当对象的初始化非常耗费资源时。 - 构造过程复杂,需要很多步骤,或者要访问远程服务的情况下。 - 需要保护性拷贝的场景,例如防止客户程序更改源对象。
原型模式一般包括两种角色: - 原型(Prototype):它是一个接口,声明了克隆方法。 - 具体原型(Concrete Prototype):实现克隆方法以复制自身。
5.1.2 原型模式的关键实现技术
实现原型模式的关键在于克隆操作。克隆分为浅克隆和深克隆两种类型: - 浅克隆(Shallow Clone):创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。 - 深克隆(Deep Clone):创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Java中实现深克隆的一种方法是重写 clone() 方法,并对引用类型进行递归拷贝。例如:
class MyObject implements Cloneable {
private int[] array;
private MyObject deepObject;
// Getter and setter methods
public MyObject clone() throws CloneNotSupportedException {
MyObject clone = (MyObject) super.clone();
clone.array = this.array.clone();
clone.deepObject = this.deepObject.clone();
return clone;
}
}
在上述代码中, clone() 方法首先调用 super.clone() 获取浅克隆对象,然后对数组和引用类型的对象进行深拷贝。
5.2 原型模式在性能优化中的作用
5.2.1 性能优化的考虑点
在性能优化中,原型模式的应用主要集中在减少对象创建的开销。对于那些创建成本较高的对象,使用原型模式可以显著减少资源消耗和系统延迟。这在大型系统和资源受限的环境中尤为重要。
考虑以下使用场景: - 对象创建代价高,如数据库连接、网络资源的获取。 - 系统对性能要求极高,如高频读操作的缓存系统。 - 对象状态不经常改变,但是创建成本高。
5.2.2 原型模式对性能影响的案例分析
假设有一个应用需要频繁地创建相似的图形对象,每个图形对象的创建都涉及到复杂的逻辑和资源分配。如果每次需要一个新对象时都通过构造函数来创建,那么性能将成为瓶颈。
这时,原型模式可以大展身手: 1. 定义一个图形接口,声明克隆方法。 2. 创建一个具体的图形类实现该接口,并实现深克隆。 3. 当需要新图形对象时,从一个已有的原型对象复制,而不是新建。
public interface Shape extends Cloneable {
public Object clone();
}
public class Circle implements Shape {
@Override
public Object clone() {
Circle cloned = null;
try {
cloned = (Circle) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return cloned;
}
}
在这个案例中,每次需要新的圆形对象时,只需调用 clone() 方法即可获得一个完全独立的实例,大大减少了资源消耗和时间开销。
通过原型模式的应用,应用的性能得到了显著的提升,尤其是在需要大量创建对象的场景下。由于避免了重复的初始化代码,系统的总体响应时间也得到了优化。
6. 适配器模式与接口转换
适配器模式是设计模式中非常重要的一个模式,主要用于不同接口之间的转换问题,它使得原本由于接口不兼容而不能工作的类可以一起工作。适配器模式能够对一个类的接口进行适配,以便于与另一个接口相匹配。这样的模式不仅适用于软件设计,在日常生活中的应用也十分广泛,如电源适配器使得国外的电器可以在国内的电源插座上使用。本章将深入探讨适配器模式的理论基础以及接口转换的实践技巧。
6.1 适配器模式的理论基础
6.1.1 适配器模式的分类及应用场景
适配器模式主要有两种形式:类适配器模式和对象适配器模式。
- 类适配器模式 :通过创建一个继承自原始类(被适配类)和实现目标接口的适配器类,来实现接口转换。这种模式会将被适配类的接口转换成目标接口,使用继承实现。
- 对象适配器模式 :通过组合一个或多个被适配类来实现目标接口。对象适配器使用组合而非继承来实现适配,更适合处理有多个被适配类的情况。
适配器模式在以下场景中非常有用: - 当你试图使用一个已经存在的类,而它的接口并不符合你的需求。 - 当你需要创建一个可以复用的类,该类可以与其他不相关的或不可预见的类一起工作。 - 当你需要使用第三方库中的类,但它的接口与你的应用不兼容时。
6.1.2 适配器模式的实现要点
适配器模式的实现要点主要有以下几点:
- 确定需要适配的类和目标接口 :这是适配器模式的两个关键部分,确定了这一点后,适配器类就可以围绕这两个关键部分进行设计。
- 设计适配器类 :设计适配器类时需要注意代码的可读性和可维护性。适配器类不应该仅仅是将接口转换的黑盒子,还应该为转换逻辑提供清晰的注释和文档。
- 处理不同类型的适配需求 :适配器模式可以用来适配一个类或者一个接口。如果需要适配多个类,可以使用多个适配器或者一个适配器适配一个接口,然后由这个接口再适配其他类。
6.2 接口转换的实践技巧
6.2.1 接口不兼容问题的解决方案
在处理接口不兼容问题时,首先需要识别目标接口和被适配类之间的差异。差异可能涉及方法名称、方法参数、返回类型、方法行为等方面。对于这些差异,可以使用适配器模式提供的方法来解决:
- 为被适配类添加新方法 :在适配器中实现目标接口的方法,并在其中调用被适配类的相应方法。
- 映射参数 :如果方法的参数不匹配,适配器需要进行适当的参数转换或映射。
- 转换返回类型 :对于返回类型不一致的情况,适配器需要将返回值转换为目标接口所需的类型。
6.2.2 适配器模式与其他设计模式的结合
适配器模式与其他设计模式结合使用,可以解决更复杂的问题。例如:
- 与工厂模式结合 :当适配器类的创建过程比较复杂时,可以使用工厂模式来创建适配器实例。
- 与装饰器模式结合 :在需要对一个对象进行扩展功能的同时又需要适配到另一个接口时,可以先使用装饰器模式对原始对象进行增强,然后通过适配器模式将增强的对象适配到目标接口。
- 与外观模式结合 :适配器模式可以用来简化接口的使用,而外观模式可以用来提供一个简化的接口来访问复杂的子系统。它们都可以使客户端代码与复杂的系统细节隔离开来。
示例代码展示
以一个常见的适配器模式实现为例,我们将创建一个可以将 Rectangle 接口适配到 Shape 接口的适配器类:
// 目标接口
interface Shape {
void draw(String color);
}
// 被适配的类
class Rectangle {
public void drawRect(String color) {
System.out.println("Rectangle drawn with color: " + color);
}
}
// 适配器类
class RectangleAdapter implements Shape {
private Rectangle rect;
public RectangleAdapter(Rectangle rect) {
this.rect = rect;
}
@Override
public void draw(String color) {
rect.drawRect(color);
}
}
// 客户端代码
public class AdapterPatternDemo {
public static void main(String[] args) {
Shape shape = new RectangleAdapter(new Rectangle());
shape.draw("red");
}
}
上述代码展示了一个简单的适配器模式实现, RectangleAdapter 作为适配器,使得 Rectangle 类能够在 Shape 接口上使用。
适配器模式在接口转换方面具有广泛的用途,通过以上介绍,我们可以理解其理论基础和实践技巧,并在实际开发中灵活应用,解决接口兼容性问题。
7. 装饰器模式与职责添加
装饰器模式是一种用于动态地给一个对象添加一些额外的职责,而不改变其结构的设计模式。它提供了一种灵活地扩展对象功能的方式,而且不会影响到从该类中派生的其他对象。
7.1 装饰器模式的原理与优势
7.1.1 装饰器模式的基本概念
装饰器模式的核心思想是在保持原有对象的基础上,通过嵌套的方式将一个装饰器对象附加到另一个对象上。装饰器类和具体组件类都继承自同一个抽象类或实现同一个接口,装饰器类在保持接口定义的前提下,增加新的方法和属性,从而达到增强组件功能的目的。
这里是一个简单的装饰器模式的代码示例:
interface Component {
void operation();
}
class ConcreteComponent implements Component {
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component c) {
***ponent = c;
}
public void operation() {
component.operation();
}
}
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component c) {
super(c);
}
public void addedBehavior() {
System.out.println("ConcreteDecorator added behavior");
}
public void operation() {
super.operation();
addedBehavior();
}
}
7.1.2 装饰器模式与继承的对比
装饰器模式提供了一种与直接使用继承不同的方法来扩展功能。继承是一种静态的行为,而装饰器模式允许通过组合来动态地添加额外的行为。使用装饰器模式,我们可以避免直接修改原有类的行为,这样做的好处是能够保持原有类的稳定性和复用性。
例如,如果我们使用继承来扩展 ConcreteComponent 的功能,那么我们可能需要创建一个新的类来继承 ConcreteComponent ,然后添加新的方法。这样做会增加类的数量,并且可能会破坏原有的类层次结构。
7.2 职责动态添加的实践策略
7.2.1 动态添加职责的场景分析
动态添加职责的场景非常广泛。例如,一个图形用户界面(GUI)组件可能需要在不同的上下文中拥有不同的行为,如在不同的平台或者不同的用户权限下,可以使用装饰器模式来动态地增加或改变组件的行为。
7.2.2 设计模式在职责添加中的应用实例
以下是一个装饰器模式的实际应用案例,假设我们需要为一个基本的日志系统动态添加功能,比如时间戳、安全性检查等。
class LoggingDecorator extends Decorator {
public LoggingDecorator(Component c) {
super(c);
}
public void operation() {
addLogging();
super.operation();
}
private void addLogging() {
System.out.println("Adding logging decorator");
}
}
class SecurityDecorator extends Decorator {
public SecurityDecorator(Component c) {
super(c);
}
public void operation() {
checkSecurity();
super.operation();
}
private void checkSecurity() {
System.out.println("Checking security before operation");
}
}
在这个案例中,我们有两个装饰器类 LoggingDecorator 和 SecurityDecorator ,它们都继承自 Decorator 。这样,我们可以将这些装饰器按需组合使用,例如:
Component component = new ConcreteComponent();
component = new SecurityDecorator(new LoggingDecorator(component));
component.operation();
上面的代码首先创建了一个具体的组件 ConcreteComponent ,然后通过 LoggingDecorator 和 SecurityDecorator 装饰了该组件,从而在不修改组件本身的情况下动态地添加了日志和安全检查的功能。这样的实现方式提供了极大的灵活性和可扩展性。
装饰器模式通过组合而不是继承的方式来扩展功能,这使得我们可以很容易地在运行时选择不同的装饰器,从而实现对原有对象功能的灵活扩展。
简介:设计模式是软件工程中的最佳实践,它们为解决常见的设计问题提供可重用的解决方案。本压缩包提供23种经典设计模式的C#实现,并探讨其在实际开发中的应用。学习这些设计模式有助于开发者编写更高效、可维护的代码,并在实际项目中通过灵活应用多种模式提升代码质量和可维护性。

1万+

被折叠的 条评论
为什么被折叠?



