简介:《C#.Sharp.Design.Pattern》详细讲解了软件工程中经典的23种设计模式,并展示了如何在.NET框架下使用C#语言进行实现。本资源通过实例代码和应用场景深入剖析了每种设计模式,旨在提升C#开发者在软件设计方面的灵活性、可维护性和可扩展性。
1. C#设计模式概述
C#作为面向对象编程语言的代表之一,在软件开发中扮演着极其重要的角色。设计模式作为解决常见软件设计问题的通用模板,对于提升软件的可维护性、可扩展性和降低复杂度有着至关重要的作用。在C#开发中,设计模式的应用不仅能够帮助开发者写出更优雅的代码,还能提高代码的复用性和系统的灵活性。本文将从设计模式的基本概念出发,深入探讨在C#环境下创建型、结构型和行为型设计模式的实现与应用,以及如何在现代软件开发实践中利用模式组合和优化来解决实际问题。接下来,让我们一步步揭开设计模式的神秘面纱,探索它们在C#世界中的力量和魅力。
2. 创建型模式的实现与应用
2.1 单例模式的实现
2.1.1 单例模式的基本原理
单例模式(Singleton Pattern)是一种常见的设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点。这种模式通常用于管理配置信息、日志记录、数据库连接等资源。
在C#中实现单例模式通常有几种不同的方法,如懒汉式、饿汉式、双重检查锁定等。懒汉式在第一次调用时实例化对象,而饿汉式则在类加载时就实例化对象,双重检查锁定则尝试结合二者的优势,减少不必要的同步开销。
2.1.2 单例模式的线程安全实现
线程安全的单例模式实现是保证在多线程环境下,不会产生多个实例的关键。这通常涉及到锁的使用,确保在实例化对象的过程中不会被其他线程干扰。
下面是一个使用双重检查锁定的线程安全单例模式实现示例:
public sealed class Singleton
{
private static volatile Singleton instance;
private static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}
这段代码通过 volatile
关键字保证了 instance
字段的读写操作都是原子操作,同时 lock
关键字确保了在创建实例时的线程安全。
2.2 工厂模式及抽象工厂模式
2.2.1 工厂模式的核心思想
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种在不暴露对象创建逻辑的情况下,创建对象的最佳方式。工厂模式通过定义一个用于创建对象的接口,让子类决定实例化哪一个类。
它主要分为三种类型: - 简单工厂模式(Simple Factory) - 工厂方法模式(Factory Method) - 抽象工厂模式(Abstract Factory)
每种模式有其特定的应用场景和优缺点,简单工厂模式适用于产品种类固定的情况,工厂方法模式提供了更好的扩展性,抽象工厂模式则是为了创建一系列相关或相互依赖的对象。
2.2.2 抽象工厂模式的高级应用
抽象工厂模式是一种创建型设计模式,它提供了一种方式,可以创建一系列相关或相互依赖的对象,而无需指定这些对象具体的类。抽象工厂模式对工厂方法模式进行了抽象和扩展,适用于有多个产品族,并且每个产品族中都有多个产品的场景。
在C#中实现抽象工厂模式通常涉及四个角色: - 抽象工厂(AbstractFactory) - 具体工厂(ConcreteFactory) - 抽象产品(AbstractProduct) - 具体产品(ConcreteProduct)
以下是抽象工厂模式的一个基础实现框架:
public interface IAbstractFactory
{
IAbstractProductA CreateProductA();
IAbstractProductB CreateProductB();
}
public interface IAbstractProductA { }
public interface IAbstractProductB { }
public class ConcreteFactory1 : IAbstractFactory
{
public IAbstractProductA CreateProductA()
{
return new ProductA1();
}
public IAbstractProductB CreateProductB()
{
return new ProductB1();
}
}
public class ConcreteFactory2 : IAbstractFactory
{
public IAbstractProductA CreateProductA()
{
return new ProductA2();
}
public IAbstractProductB CreateProductB()
{
return new ProductB2();
}
}
public class ProductA1 : IAbstractProductA { }
public class ProductA2 : IAbstractProductA { }
public class ProductB1 : IAbstractProductB { }
public class ProductB2 : IAbstractProductB { }
在上述代码中, IAbstractFactory
定义了创建产品的方法, ConcreteFactory1
和 ConcreteFactory2
是具体工厂,它们分别创建了对应的产品系列。这种方式使得在不修改现有代码的情况下,添加新的产品族变得非常容易。
2.3 建造者模式的应用
2.3.1 建造者模式的特点与适用场景
建造者模式(Builder Pattern)是一种创建型设计模式,它提供了一种创建复杂对象的最佳方式。建造者模式通过指定不同的建造步骤和顺序,可以创建不同的表示,并使同样的构建过程可以创建不同的产品。
建造者模式特别适用于以下场景: - 创建的对象结构较为复杂,需要通过多个步骤进行构建。 - 产品的构建过程需要灵活多变,不同的构建顺序可以产生不同的产品配置。
2.3.2 建造者模式的实践案例分析
以一个简单的房屋建造场景为例,房屋建筑过程可以分解为地基、墙体、屋顶等步骤。在C#中可以这样实现建造者模式:
public class House
{
public string Foundation { get; set; }
public string Walls { get; set; }
public string Roof { get; set; }
}
public interface IHouseBuilder
{
House BuildFoundation();
House BuildWalls();
House BuildRoof();
House GetResult();
}
public class SmallHouseBuilder : IHouseBuilder
{
private House house = new House();
public House BuildFoundation()
{
house.Foundation = "Small house foundation";
return house;
}
public House BuildWalls()
{
house.Walls = "Small house walls";
return house;
}
public House BuildRoof()
{
house.Roof = "Small house roof";
return house;
}
public House GetResult()
{
return house;
}
}
在这个例子中, House
类表示产品, IHouseBuilder
接口定义了构建产品的步骤, SmallHouseBuilder
类实现了这些步骤并返回最终构建的 House
对象。这样,通过调用不同的构建方法,可以灵活地完成不同类型的房屋构建过程。
3. 结构型模式的深入探讨
3.1 适配器模式的实现
3.1.1 适配器模式的基本概念
适配器模式是一种结构型设计模式,它允许将一个类的接口转换成客户端期望的另一个接口。适配器模式的关键角色包括目标接口(Target)、需要适配的类(Adaptee)和适配器(Adapter)本身。适配器会将Adaptee的接口转换为Target接口,从而允许原本接口不兼容的类能够协同工作。
适配器模式通常用在以下几种情况:
- 当你想要使用一个已经存在的类,而它的接口不符合你的需求。
- 当你需要创建一个可以与不相关的或不可预见的类协同工作的类。
- 当你需要提供一个接口,而其他对象的接口实现部分并不兼容。
3.1.2 适配器模式在实际开发中的应用
适配器模式在实际应用中有广泛的应用场景。例如,在.NET开发中,你可能需要将第三方库的接口适配到你的系统中。下面是一个简单的适配器模式实现示例。
假设有一个第三方库提供的接口如下:
public interface IThirdPartyLibrary
{
void OldFashionedMethod();
}
而你的系统要求的接口是这样的:
public interface INewSystemInterface
{
void ModernMethod();
}
由于接口不兼容,我们可以创建一个适配器来解决这个问题:
public class Adapter : INewSystemInterface
{
private IThirdPartyLibrary _adaptee;
public Adapter(IThirdPartyLibrary adaptee)
{
this._adaptee = adaptee;
}
public void ModernMethod()
{
// 假设OldFashionedMethod()方法内部实现了ModernMethod()所需的功能
_adaptee.OldFashionedMethod();
}
}
使用时,我们只需要创建适配器实例,并通过它调用新系统接口即可:
IThirdPartyLibrary oldSystem = new ConcreteThirdPartyClass();
INewSystemInterface newSystem = new Adapter(oldSystem);
newSystem.ModernMethod();
这个简单的例子说明了适配器模式的核心思想:通过封装一个对象,来将不兼容的接口转换成兼容的接口。
3.2 装饰器模式与扩展方法
3.2.1 装饰器模式的结构与功能
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
装饰器模式通常包含以下几个主要组件:
- Component(组件) :一个抽象接口,用于给对象动态地添加职责。
- ConcreteComponent(具体组件) :实现了Component接口的具体类。
- Decorator(装饰器) :持有Component的一个引用,并实现了Component接口。
- ConcreteDecorator(具体装饰器) :具体的装饰对象,实现了装饰组件的额外行为。
3.2.2 扩展方法在C#中的实现
C#提供了一种便利的特性叫做扩展方法,它允许开发者为现有的类型添加新的方法,而无需修改原始类的源代码。扩展方法是用静态类实现的,以 this
为前缀的方法参数指定要扩展的类型。
下面是一个扩展方法的实现示例:
public static class StringExtensions
{
public static int WordCount(this String str)
{
return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length;
}
}
class Program
{
static void Main(string[] args)
{
string s = "Hello there";
Console.WriteLine($"The number of words is {s.WordCount()}");
}
}
上述示例中, StringExtensions
类中定义了一个扩展方法 WordCount
,这个方法扩展了 String
类型。在 Main
方法中,我们调用了字符串 s
的 WordCount
方法,就像它是 String
类自带的方法一样。
装饰器模式和扩展方法在功能上有所相似,但它们的应用场景和实现机制有差异。扩展方法适用于给单个类型添加方法,而装饰器模式适用于添加一系列相关的新行为到对象的集合上。装饰器模式通过创建包装对象,使用组合而不是继承来动态地给对象添加功能。
3.3 代理模式与.NET接口实现
3.3.1 代理模式的设计意图
代理模式是又一种结构型设计模式,它的目的是为其他对象提供一个代理以控制对这个对象的访问。在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。代理模式主要解决的是如何提供一个统一的接口,来访问不同的对象,同时增加控制点,简化客户端调用。
代理模式的关键角色包括:
- Subject(主题) :定义了RealSubject和Proxy的共用接口,这样就可以在任何使用RealSubject的地方使用Proxy。
- RealSubject(真实主题) :定义了Proxy所代表的真实实体。
- Proxy(代理) :包含一个与Subject的接口相同的引用,以便可以在任何时候都引用和访问实体。它还可以控制对真实主题的访问,并且负责在需要时创建和删除真实主题。
***接口的应用实例
在.NET中,接口是定义一组方法规范的引用类型,它与C#中的类不同,因为它不提供方法的具体实现。接口被用来达到多态的目的。下面是一个.NET接口的应用实例:
public interface IShapes
{
void Draw();
}
public class Rectangle : IShapes
{
public void Draw()
{
Console.WriteLine("Shape: Rectangle");
}
}
public class Circle : IShapes
{
public void Draw()
{
Console.WriteLine("Shape: Circle");
}
}
public class ShapeProxy : IShapes
{
private IShapes _realShape;
public ShapeProxy(IShapes realShape)
{
_realShape = realShape;
}
public void Draw()
{
// 在绘图之前可以添加一些额外的逻辑
Console.WriteLine("Before drawing shape");
_realShape.Draw();
// 在绘图之后也可以添加一些额外的逻辑
Console.WriteLine("After drawing shape");
}
}
class Program
{
static void Main(string[] args)
{
IShapes shape = new ShapeProxy(new Rectangle());
shape.Draw();
}
}
在这个例子中,我们定义了一个 IShapes
接口,它有两个具体的实现 Rectangle
和 Circle
。我们还定义了一个 ShapeProxy
类,它实现 IShapes
接口,并且内部持有一个 IShapes
类型的引用。通过代理模式,我们在实际绘制形状之前和之后都增加了一些额外的逻辑。这样,我们可以在不影响原有类结构的情况下,对绘制操作进行控制和增强。
4. 行为型模式的原理与实践
行为型模式关注的是对象之间的通信,它定义了对象间的一种交互方式。行为型模式的类型很多,涵盖了不同层面的通信和控制机制。本章节将详细介绍行为型模式的原理及实践,并通过具体的案例来展示它们在实际开发中的应用。
4.1 职责链模式与事件处理
4.1.1 职责链模式的定义与组成
职责链模式(Chain of Responsibility)是一种行为设计模式,它将请求的发送者和接收者解耦,使得多个对象都有机会处理这个请求。这个模式通过定义一条链,使得请求沿着这条链进行传递,直到被其中的一个对象处理。
职责链模式由以下几个核心部分组成:
- 处理器(Handler) :定义一个处理请求的接口,通常是一个抽象类,也可以是一个具体的类。此外,它还实现了后继处理者的引用。
- 具体处理器(Concrete Handler) :处理它所负责的请求,如果无法处理,则将请求传递给链中的下一个处理者。
- 客户端(Client) :向链式结构的起始处理器发送请求。
4.1.2 事件处理机制中的职责链模式
在.NET中,事件处理机制可以看作是一种职责链模式的实现。当一个事件被触发时,如果绑定了多个事件处理器,那么它们将按照绑定的顺序依次被调用。在这个过程中,事件处理器之间的调用顺序就构成了一个职责链。
以Windows窗体应用程序为例,当一个按钮被点击时:
button.Click += new EventHandler(Handler1);
button.Click += new EventHandler(Handler2);
void Handler1(object sender, EventArgs e)
{
// 处理事件逻辑
}
void Handler2(object sender, EventArgs e)
{
// 处理事件逻辑
}
在这个例子中,如果 Handler1
无法完全处理点击事件,它不会阻止事件传递给 Handler2
。事件的这种处理方式恰好体现了职责链模式的特点。
代码逻辑解读:
-
+=
操作符用于将事件处理器添加到事件的调用列表中。 -
button.Click
是Windows窗体控件的点击事件。 -
Handler1
和Handler2
是事件处理器,它们被添加到按钮点击事件的职责链中。
4.2 命令模式与委托、事件
4.2.1 命令模式的结构和实例
命令模式(Command Pattern)将“请求”封装成一个对象,从而允许使用不同的请求、队列或者日志请求来参数化其他对象。命令模式还支持可撤销的操作。
命令模式主要包含以下四个角色:
- 调用者(Invoker) :请求的发送者,它持有命令对象,并通过命令对象来调用接收者相应的操作。
- 命令(Command) :声明执行操作的接口。
- 具体命令(Concrete Command) :将一个接收者对象绑定于一个动作,实现
Command
接口。调用接收者相应的操作,以实现execute
等方法。 - 接收者(Receiver) :知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。
4.2.2 委托和事件在命令模式中的运用
在C#中,委托是一种可以持有对某个方法的引用的数据类型,它可以将方法作为参数传递给其他方法,或者从方法中返回。事件是一种特殊的多播委托,它表示发生了一个事件,而且可以有多个方法来响应它。
例如,一个命令对象可以被表示为一个委托类型,调用者可以调用这个委托来执行命令:
public delegate void CommandDelegate();
public class Light
{
public void TurnOn() { /* ... */ }
public void TurnOff() { /* ... */ }
}
public class LightOnCommand
{
private Light light;
public LightOnCommand(Light light)
{
this.light = light;
}
public void Execute()
{
light.TurnOn();
}
}
// ...
CommandDelegate command = new CommandDelegate(new LightOnCommand(new Light()).Execute);
command += new CommandDelegate(new LightOffCommand(new Light()).Execute);
在这个例子中, CommandDelegate
是一个委托, LightOnCommand
和 LightOffCommand
是具体命令类,它们实现了命令接口并关联了一个接收者对象(此处为 Light
类)。
代码逻辑解读:
-
CommandDelegate
定义了一个委托,其方法签名与命令接口一致。 -
LightOnCommand
和LightOffCommand
实现了命令接口,并且在Execute
方法中调用了Light
类的TurnOn
和TurnOff
方法。 - 在最后的代码块中,通过将具体命令对象的
Execute
方法绑定到委托上,创建了一个命令链,这个命令链可以像职责链一样顺序执行。
4.3 策略模式与算法封装
4.3.1 策略模式的基本原理
策略模式(Strategy Pattern)定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响到使用算法的客户端。
策略模式包含以下三个角色:
- 策略(Strategy) :定义所有支持的算法的公共接口。
- 具体策略(Concrete Strategy) :实现了算法的接口。
- 上下文(Context) :持有策略的引用,并可以根据需要切换策略。
4.3.2 算法封装在策略模式中的实现
策略模式的关键是使用组合而非继承来处理算法的变化,这使得算法可以在运行时动态地改变。
举例来说,一个付款策略的实现如下:
public interface IPaymentStrategy
{
void ProcessPayment(decimal amount);
}
public class CreditCardPayment : IPaymentStrategy
{
public void ProcessPayment(decimal amount)
{
// 处理信用卡支付逻辑
}
}
public class PayPalPayment : IPaymentStrategy
{
public void ProcessPayment(decimal amount)
{
// 处理PayPal支付逻辑
}
}
// ...
public class ShoppingCart
{
private List<Products> products;
private IPaymentStrategy paymentStrategy;
public ShoppingCart(IPaymentStrategy paymentStrategy)
{
this.products = new List<Products>();
this.paymentStrategy = paymentStrategy;
}
public void AddItem(Products product)
{
this.products.Add(product);
}
public void Checkout(decimal amount)
{
this.paymentStrategy.ProcessPayment(amount);
}
}
代码逻辑解读:
-
IPaymentStrategy
定义了一个支付策略接口,包含一个ProcessPayment
方法。 -
CreditCardPayment
和PayPalPayment
实现了IPaymentStrategy
接口,分别定义了不同的支付方式。 -
ShoppingCart
类持有一个IPaymentStrategy
的引用,并在Checkout
方法中调用该策略的支付方法。 - 客户端可以根据不同的支付需求,向
ShoppingCart
传递不同的支付策略实现。
策略模式的这种封装使得业务逻辑的处理更加灵活,易于扩展新算法,并且可以轻松地在运行时更改行为。
以上章节深入探讨了行为型设计模式中的职责链模式、命令模式和策略模式。通过代码示例和逻辑解读,展现了每种模式的实现方法和在实际开发中的应用方式。这些模式在不同层面提供了解决问题的思路,对于构建可扩展、可维护的软件系统具有重要的实践意义。
5. 模式的组合与扩展
组合模式与树形结构是设计模式中非常重要的概念,它们不仅能够帮助开发者构建复杂的系统结构,而且还能在保持系统灵活性的同时,提升代码的可复用性和可维护性。接下来,我们将深入探讨这些模式。
5.1 组合模式与树形结构
组合模式是一种用于组织具有相同类型和接口的对象树形结构的模式。它允许用户以统一的方式处理单个对象和组合对象。
5.1.1 组合模式的组成和适用场景
组合模式主要由三部分组成:组件(Component)、叶子(Leaf)和复合体(Composite)。组件定义了对象的公共接口,可以是单独的对象也可以是对象的容器。叶子代表了树形结构的末端对象,而复合体则包含了子组件。
在实际的应用中,组合模式非常适用于处理具有层级关系的数据结构,例如文件系统的目录结构、UI控件的层次组织等。
// 组件接口
public interface IComponent
{
void Operation();
}
// 叶子节点
public class Leaf : IComponent
{
public void Operation()
{
Console.WriteLine("Leaf Operation");
}
}
// 复合体
public class Composite : IComponent
{
private List<IComponent> _children = new List<IComponent>();
public void Add(IComponent component)
{
_children.Add(component);
}
public void Remove(IComponent component)
{
_children.Remove(component);
}
public void Operation()
{
foreach (var child in _children)
{
child.Operation();
}
}
}
5.1.2 树形结构在组合模式中的应用
树形结构可以在很多地方找到,比如组织架构、计算机文件系统、网络、UI布局等。组合模式在这些场景中,为单个对象和组合对象提供了一个统一的操作接口。
在C#中,我们可以利用组合模式来管理一个复杂的UI布局,其中每个UI控件既可以是一个独立的组件,也可以是一个包含多个子控件的容器。
public class UIComposite : Composite
{
private string _name;
public UIComposite(string name)
{
_name = name;
}
public override void Operation()
{
Console.WriteLine($"Composite {_name} is being operated");
base.Operation();
}
}
public class Program
{
public static void Main()
{
var root = new UIComposite("Root");
var composite1 = new UIComposite("Composite 1");
var composite2 = new UIComposite("Composite 2");
var leaf = new Leaf();
root.Add(composite1);
root.Add(composite2);
composite1.Add(leaf);
root.Operation();
}
}
5.2 访问者模式与类结构扩展
访问者模式是一种行为型设计模式,它允许你对一个对象结构中的元素添加新的操作,而无需改变这些元素的类。
5.2.1 访问者模式的定义和功能
访问者模式提供了一种方法,能够在一个已存在的类结构中加入新的操作,而无需修改现有代码。访问者模式将数据结构和数据操作分离,这在系统需要频繁地添加新操作而这些操作又不修改数据结构时非常有用。
访问者模式主要包含访问者(Visitor)、具体访问者(ConcreteVisitor)、元素(Element)、具体元素(ConcreteElement)等角色。
// 访问者接口
public interface IVisitor
{
void VisitConcreteElementA(ConcreteElementA elementA);
void VisitConcreteElementB(ConcreteElementB elementB);
}
// 具体访问者
public class ConcreteVisitor : IVisitor
{
public void VisitConcreteElementA(ConcreteElementA elementA)
{
elementA.OperationA();
}
public void VisitConcreteElementB(ConcreteElementB elementB)
{
elementB.OperationB();
}
}
// 元素接口
public interface IElement
{
void Accept(IVisitor visitor);
}
// 具体元素
public class ConcreteElementA : IElement
{
public void Accept(IVisitor visitor)
{
visitor.VisitConcreteElementA(this);
}
public void OperationA()
{
Console.WriteLine("ConcreteElementA OperationA");
}
}
public class ConcreteElementB : IElement
{
public void Accept(IVisitor visitor)
{
visitor.VisitConcreteElementB(this);
}
public void OperationB()
{
Console.WriteLine("ConcreteElementB OperationB");
}
}
5.2.2 类结构扩展的策略与实现
当系统的类结构相对稳定,而操作需要频繁变化时,访问者模式提供了一个很好的扩展策略。通过添加新的具体访问者,开发者可以实现新的操作而不需要修改现有的类。
例如,如果我们有一个绘图系统,其中包含多种图形对象如圆形、矩形等。如果想添加一个新的功能,比如输出不同图形的XML表示,我们可以通过实现一个新的访问者来完成,而无需修改现有的图形类。
// 新功能访问者
public class XMLVisitor : IVisitor
{
public void VisitConcreteElementA(ConcreteElementA elementA)
{
Console.WriteLine($"Converting {elementA.GetType().Name} to XML");
}
public void VisitConcreteElementB(ConcreteElementB elementB)
{
Console.WriteLine($"Converting {elementB.GetType().Name} to XML");
}
}
// 扩展类结构使用新的访问者
public class Program
{
public static void Main()
{
var visitor = new XMLVisitor();
var elementA = new ConcreteElementA();
var elementB = new ConcreteElementB();
elementA.Accept(visitor);
elementB.Accept(visitor);
}
}
通过使用访问者模式,我们可以非常灵活地为系统添加新的行为,而且不会影响现有的类结构,这在大型系统中尤为重要。
6. 模式的优化与高级特性
在软件开发的过程中,设计模式不仅仅是用来解决特定问题的一套解决方案,还应该随着时间的推移而进化,适应更复杂的场景,以及利用语言提供的高级特性来进一步优化。本章将探讨如何利用C#语言的特性,比如多态、接口等,对现有的设计模式进行优化,并挖掘这些模式的高级应用。
6.1 桥接模式与多态特性
桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化。多态则是面向对象编程的一个重要特性,它允许开发者用相同的接口来处理不同的基本操作。
6.1.1 桥接模式解决的问题
桥接模式的核心思想是通过定义一个抽象和其一个实现层次,将抽象的实现细节从抽象中分离出来,使得它们可以独立变化。通过这种方式,抽象和实现可以分别进行扩展而不影响对方。
public interface Implementation
{
void OperationImpl();
}
public class ConcreteImplementationA : Implementation
{
public void OperationImpl()
{
Console.WriteLine("具体实现A的方法被调用");
}
}
public class ConcreteImplementationB : Implementation
{
public void OperationImpl()
{
Console.WriteLine("具体实现B的方法被调用");
}
}
在上面的代码中, Implementation
是一个抽象的实现层次,而 ConcreteImplementationA
和 ConcreteImplementationB
则是具体的实现。桥接模式可以使得 Abstraction
(抽象部分)和 Implementation
(实现部分)独立变化。
6.1.2 多态在桥接模式中的应用
多态性在桥接模式中的应用体现在,当调用 Abstraction
中的方法时,最终调用的是 Implementation
层次的方法。这样,不同的 Implementation
可以在运行时被替换,而无需修改使用 Abstraction
的代码。
public abstract class Abstraction
{
protected Implementation implementation;
public Abstraction(Implementation implementation)
{
this.implementation = implementation;
}
public abstract void Operation();
}
public class RefinedAbstraction : Abstraction
{
public RefinedAbstraction(Implementation implementation) : base(implementation) { }
public override void Operation()
{
Console.WriteLine("RefinedAbstraction: ");
implementation.OperationImpl();
}
}
在 RefinedAbstraction
类中, Operation
方法调用了 Implementation
的 OperationImpl
方法,这样做的好处是 RefinedAbstraction
本身并不关心具体是哪个实现被执行。这就允许我们通过替换 Implementation
实例来改变 Abstraction
的行为。
6.1.3 代码逻辑解读
-
Abstraction
是一个抽象类,它依赖于Implementation
类型,这样定义的目的是可以将Abstraction
和Implementation
的具体实现分离。 -
RefinedAbstraction
是Abstraction
的一个具体实现。它使用多态性通过implementation
成员来调用OperationImpl
方法,从而实现了桥接模式。 - 通过这种方式,如果需要在不同的
Implementation
之间切换,只需要传递不同的Implementation
对象到RefinedAbstraction
的构造器中即可,无需修改RefinedAbstraction
内部代码。
6.2 迭代器模式与IEnumerable接口
迭代器模式是一种行为设计模式,它提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
6.2.1 迭代器模式的设计目的
迭代器模式允许在不知道内部结构的情况下,逐一访问容器元素。C# 中的 IEnumerable
和 IEnumerator
接口就是迭代器模式的具体实现。
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}
-
IEnumerable
允许一个集合被迭代,而IEnumerator
则提供了一个方法来遍历集合。
6.2.2 IEnumerable接口的实现与应用
当一个类实现了 IEnumerable
接口,它就可以被 foreach
循环遍历,使得外部代码可以逐项访问集合元素而不需要了解集合的内部结构。
public class MyCollection : IEnumerable
{
List<object> items = new List<object>();
public IEnumerator GetEnumerator()
{
return new MyListEnumerator(this);
}
private class MyListEnumerator : IEnumerator
{
private MyCollection collection;
private int currentIndex = -1;
public MyListEnumerator(MyCollection collection)
{
this.collection = collection;
}
public bool MoveNext()
{
currentIndex++;
return currentIndex < collection.items.Count;
}
public object Current
{
get
{
if (currentIndex == -1 || currentIndex >= collection.items.Count)
throw new InvalidOperationException();
return collection.items[currentIndex];
}
}
public void Reset()
{
currentIndex = -1;
}
}
}
在上述代码中, MyCollection
类实现了 IEnumerable
接口, MyListEnumerator
类实现了 IEnumerator
接口。这样, MyCollection
的实例就可以在 foreach
循环中使用。
6.2.3 代码逻辑解读
-
MyCollection
类内部封装了一个List<object>
类型的集合。 -
MyCollection
类的GetEnumerator
方法返回了一个MyListEnumerator
对象。这个对象持有一个当前遍历到的索引。 -
MyListEnumerator
类的MoveNext
方法根据当前索引移动到下一个元素,如果到达集合末尾则返回false
。 -
Current
属性返回当前元素的值,如果索引无效则抛出异常。 -
Reset
方法将索引重置为初始状态。 - 这样的实现使得外部代码可以使用
foreach
循环,而无需关心集合的内部细节。
迭代器模式和 IEnumerable
接口的结合,使得集合类的使用者可以更加方便地遍历元素,同时保证了集合的封装性。这些高级特性的使用不仅优化了代码的可读性和可维护性,还提高了开发效率。在本章节中,我们深入探讨了桥接模式与多态性的结合,以及迭代器模式和 IEnumerable
接口在C#中的实现。通过实际代码示例,我们不仅理解了模式背后的原理,而且掌握了如何将这些模式应用于实际开发中,以提高代码质量和可扩展性。在后续的章节中,我们将进一步探索模式在真实场景中的应用案例,以及如何将这些模式与其他设计模式结合使用,以解决更复杂的编程问题。
7. 模式在实际开发中的应用案例
在软件开发中,设计模式不仅仅是一种理论上的概念,它们在实际的应用中能够提供解决实际问题的有效方法。通过前面章节对设计模式的探讨,我们现在将深入分析这些模式在真实世界项目中的应用案例。
7.1 状态模式与多态条件语句
7.1.1 状态模式的实现原理
状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。这是通过引入状态接口和具体状态子类来实现的。
public interface IState
{
void Handle(Context context);
}
public class ConcreteStateA : IState
{
public void Handle(Context context)
{
context.SetState(new ConcreteStateB());
}
}
public class Context
{
private IState _state;
public Context(IState state)
{
_state = state;
}
public void Request()
{
_state.Handle(this);
}
public void SetState(IState state)
{
_state = state;
}
}
7.1.2 多态条件语句在状态管理中的应用
在状态模式中,多态条件语句可以用来根据不同的状态执行不同的逻辑。
public void TransitionTo(IState newState)
{
if (newState == null)
throw new ArgumentNullException(nameof(newState));
_state = newState;
}
void ChangeState(string newState)
{
switch (newState)
{
case "StateA":
TransitionTo(new ConcreteStateA());
break;
case "StateB":
TransitionTo(new ConcreteStateB());
break;
// 更多状态的处理...
default:
throw new ArgumentOutOfRangeException(nameof(newState));
}
}
7.2 观察者模式的标准实现
7.2.1 观察者模式的设计与特性
观察者模式定义了对象间的一种一对多的依赖关系,当一个对象改变状态时,所有依赖于它的对象都会得到通知并被自动更新。
public interface IObservable
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
public interface IObserver
{
void Update(IObservable observable);
}
7.2.2 标准实现与事件驱动架构的结合
在.NET中,可以使用内置的事件来实现观察者模式,这样可以简化事件订阅和处理的代码。
public class Subject : IObservable
{
private List<IObserver> _observers = new List<IObserver>();
public void Attach(IObserver observer)
{
_observers.Add(observer);
}
public void Detach(IObserver observer)
{
_observers.Remove(observer);
}
public void Notify()
{
foreach (var observer in _observers)
{
observer.Update(this);
}
}
}
7.3 解释器模式与表达式树
7.3.1 解释器模式的构建与解释过程
解释器模式用于定义语言的文法,并为语言创建解释器。它是实现一个特定简单语言的解释器的好方法。
public interface IExpression
{
bool Interpret(string context);
}
public class TerminalExpression : IExpression
{
private string _data;
public TerminalExpression(string data)
{
_data = data;
}
public bool Interpret(string context)
{
return context.Contains(_data);
}
}
public class OrExpression : IExpression
{
private IExpression _expr1;
private IExpression _expr2;
public OrExpression(IExpression expr1, IExpression expr2)
{
_expr1 = expr1 ?? throw new ArgumentNullException(nameof(expr1));
_expr2 = expr2 ?? throw new ArgumentNullException(nameof(expr2));
}
public bool Interpret(string context)
{
return _expr1.Interpret(context) || _expr2.Interpret(context);
}
}
7.3.2 表达式树在解释器模式中的运用
.NET中表达式树的使用可以让解释器模式更加灵活和强大,尤其是在处理复杂表达式时。
// 示例使用了Lambda表达式构建表达式树
Expression<Func<int, bool>> expression = x => x > 5 && x < 10;
通过结合表达式树和解释器模式,可以轻松地构建和执行复杂的表达式,而且更容易维护和扩展。
以上案例提供了设计模式在实际开发中的应用思路,通过深入的分析和实现,我们可以根据项目的需要灵活地应用各种设计模式,解决实际问题,并优化代码结构。
简介:《C#.Sharp.Design.Pattern》详细讲解了软件工程中经典的23种设计模式,并展示了如何在.NET框架下使用C#语言进行实现。本资源通过实例代码和应用场景深入剖析了每种设计模式,旨在提升C#开发者在软件设计方面的灵活性、可维护性和可扩展性。