C#软件架构设计原则
<h1 class="md-end-block md-heading"><span class="md-plain md-expand">软件架构设计原则</span></h1><p class="md-end-block md-p">学习设计原则是学习设计模式的基础。在实际的开发过程中,并不是一定要求所有的代码都遵循设计原则,而是要综合考虑人力、成本、时间、质量,不刻意追求完美,要在适当的场景遵循设计原则。这体现的是一种平衡取舍,可以帮助我们设计出更加优雅的代码结构。</p>
<p class="md-end-block md-p">分别用一句话归纳总结软件设计七大原则,如下表所示。</p>
<table style="border-collapse: collapse; width: 100%; height: 168px" border="1">
<tbody>
<tr style="height: 21px">
<td style="width: 32.4309%; text-align: center; height: 21px">设计原则</td>
<td style="width: 32.4309%; text-align: center; height: 21px">一句话归纳</td>
<td style="width: 32.4309%; text-align: center; height: 21px">目的</td>
</tr>
<tr style="height: 21px">
<td style="width: 32.4309%; height: 21px">开闭原则</td>
<td style="width: 32.4309%; height: 21px">对扩展开放,对修改关闭</td>
<td style="width: 32.4309%; height: 21px">降低对维护带来的新风险</td>
</tr>
<tr style="height: 21px">
<td style="width: 32.4309%; height: 21px">依赖倒置原则</td>
<td style="width: 32.4309%; height: 21px">高层不应该依赖底层</td>
<td style="width: 32.4309%; height: 21px">更利于代码结构的升级扩展</td>
</tr>
<tr style="height: 21px">
<td style="width: 32.4309%; height: 21px">单一职责原则</td>
<td style="width: 32.4309%; height: 21px">一个类只干一件事</td>
<td style="width: 32.4309%; height: 21px">便于理解,提高代码的可读性</td>
</tr>
<tr style="height: 21px">
<td style="width: 32.4309%; height: 21px">接口隔离原则</td>
<td style="width: 32.4309%; height: 21px">一个接口只干一件事</td>
<td style="width: 32.4309%; height: 21px">功能解耦,高聚合、低耦合</td>
</tr>
<tr style="height: 21px">
<td style="width: 32.4309%; height: 21px">迪米特法则</td>
<td style="width: 32.4309%; height: 21px">不该知道的不要知道</td>
<td style="width: 32.4309%; height: 21px">只和朋友交流,不和陌生人说话,减少代码臃肿</td>
</tr>
<tr style="height: 21px">
<td style="width: 32.4309%; height: 21px">里氏替换原则</td>
<td style="width: 32.4309%; height: 21px">子类重写方式功能发生改变,不应该影响父类方法的含义</td>
<td style="width: 32.4309%; height: 21px">防止继承泛滥</td>
</tr>
<tr style="height: 21px">
<td style="width: 32.4309%; height: 21px">合成复用原则</td>
<td style="width: 32.4309%; height: 21px">尽量使用组合实现代码复用,而不使用继承</td>
<td style="width: 32.4309%; height: 21px">降低代码耦合</td>
</tr>
</tbody>
</table>
<h2 class="md-end-block md-heading"><span class="md-plain">开闭原则示例</span></h2>
<p class="md-end-block md-p"><span class="md-plain">当使用C#编程语言时,可以通过以下示例来说明开闭原则的应用:</span></p>
<p class="md-end-block md-p"><span class="md-plain">假设我们正在设计一个图形绘制应用程序,其中包含不同类型的图形(如圆形、矩形、三角形等)。我们希望能够根据需要轻松地添加新的图形类型,同时保持现有代码的稳定性。</span></p>
<p class="md-end-block md-p"><span class="md-plain">首先,我们定义一个抽象基类 </span><span class="md-pair-s" spellcheck="false"><code>Shape</code></span><span class="md-plain"> 来表示所有图形的通用属性和行为:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public abstract class Shape
{
public abstract void Draw();
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">然后,我们创建具体的图形类,如 </span><span class="md-pair-s" spellcheck="false"><code>Circle</code></span><span class="md-plain">、</span><span class="md-pair-s" spellcheck="false"><code>Rectangle</code></span><span class="md-plain"> 和 </span><span class="md-pair-s" spellcheck="false"><code>Triangle</code></span><span class="md-plain">,它们都继承自 </span><span class="md-pair-s" spellcheck="false"><code>Shape</code></span><span class="md-plain"> 基类,并实现了 </span><span class="md-pair-s" spellcheck="false"><code>Draw()</code></span><span class="md-plain"> 方法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle");
}
}
public class Triangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a triangle");
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">现在,如果我们需要添加新的图形类型(例如椭圆),只需创建一个新的类并继承自 </span><span class="md-pair-s" spellcheck="false"><code>Shape</code></span><span class="md-plain"> 类即可。这样做不会影响现有代码,并且可以轻松地扩展应用程序。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class Ellipse : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing an ellipse");
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">在应用程序的其他部分,我们可以使用 </span><span class="md-pair-s" spellcheck="false"><code>Shape</code></span><span class="md-plain"> 类型的对象来绘制不同的图形,而无需关心具体的图形类型。这样,我们遵循了开闭原则,对扩展开放(通过添加新的图形类型),对修改关闭(不需要修改现有代码)。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class DrawingProgram
{
public void DrawShapes(List<Shape> shapes)
{
foreach (var shape in shapes)
{
shape.Draw();
}
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">使用示例:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>var shapes = new List<Shape>
{
new Circle(),
new Rectangle(),
new Triangle(),
new Ellipse()
};
var drawingProgram = new DrawingProgram();
drawingProgram.DrawShapes(shapes);</code></pre>
<p class="md-end-block md-p"><span class="md-plain">输出结果:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="" tabindex="0" spellcheck="false"><code>Drawing a circle
Drawing a rectangle
Drawing a triangle
Drawing an ellipse</code></pre>
<p class="md-end-block md-p"><span class="md-plain">通过遵循开闭原则,我们可以轻松地扩展应用程序并添加新的图形类型,而无需修改现有代码。这样可以提高代码的可维护性和可扩展性,并支持软件系统的演化和变化。</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">单一职责示例</span></h2>
<p class="md-end-block md-p"><span class="md-plain">单一职责原则(Single Responsibility Principle,SRP)要求一个类应该只有一个引起它变化的原因。换句话说,一个类应该只负责一项职责或功能。</span></p>
<p class="md-end-block md-p"><span class="md-plain">下面是一个使用C#示例来说明单一职责原则的应用:</span></p>
<p class="md-end-block md-p"><span class="md-plain">假设我们正在开发一个学生管理系统,其中包含学生信息的录入和展示功能。我们可以将这个系统分为两个类:</span><span class="md-pair-s" spellcheck="false"><code>Student</code></span><span class="md-plain"> 和 </span><span class="md-pair-s" spellcheck="false"><code>StudentManager</code></span><span class="md-plain">。</span></p>
<p class="md-end-block md-p"><span class="md-plain">首先,定义 </span><span class="md-pair-s" spellcheck="false"><code>Student</code></span><span class="md-plain"> 类来表示学生对象,并包含与学生相关的属性和方法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class Student
{
public string Name { get; set; }
public int Age { get; set; }
// 其他与学生相关的属性和方法...
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">然后,创建 </span><span class="md-pair-s" spellcheck="false"><code>StudentManager</code></span><span class="md-plain"> 类来处理与学生信息管理相关的操作,如录入、查询和展示等:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class StudentManager
{
private List<Student> students;
public StudentManager()
{
students = new List<Student>();
}
public void AddStudent(Student student)
{
// 将学生信息添加到列表中...
students.Add(student);
Console.WriteLine("Student added successfully.");
}
public void DisplayStudents()
{
// 展示所有学生信息...
foreach (var student in students)
{
Console.WriteLine($"Name: {student.Name}, Age: {student.Age}");
}
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">在这个例子中,</span><span class="md-pair-s" spellcheck="false"><code>Student</code></span><span class="md-plain"> 类负责表示单个学生对象,并封装了与学生相关的属性。而 </span><span class="md-pair-s" spellcheck="false"><code>StudentManager</code></span><span class="md-plain"> 类负责处理学生信息的管理操作,如添加学生和展示学生信息。</span></p>
<p class="md-end-block md-p"><span class="md-plain">使用示例:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>var student1 = new Student { Name = "Alice", Age = 20 };
var student2 = new Student { Name = "Bob", Age = 22 };
var studentManager = new StudentManager();
studentManager.AddStudent(student1);
studentManager.AddStudent(student2);
studentManager.DisplayStudents();</code></pre>
<p class="md-end-block md-p"><span class="md-plain">输出结果:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="" tabindex="0" spellcheck="false"><code>Student added successfully.
Student added successfully.
Name: Alice, Age: 20
Name: Bob, Age: 22</code></pre>
<p class="md-end-block md-p"><span class="md-plain">通过将学生对象的表示和管理操作分别封装在不同的类中,我们遵循了单一职责原则。</span><span class="md-pair-s" spellcheck="false"><code>Student</code></span><span class="md-plain"> 类只负责表示学生对象的属性,而 </span><span class="md-pair-s" spellcheck="false"><code>StudentManager</code></span><span class="md-plain"> 类只负责处理与学生信息管理相关的操作。这样可以提高代码的可维护性和可扩展性,并使每个类都具有清晰明确的职责。</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">里式替换</span></h2>
<p class="md-end-block md-p"><span class="md-plain">里氏替换原则(Liskov Substitution Principle,LSP)要求子类型必须能够替换其基类型,并且不会破坏程序的正确性。也就是说,子类可以在不影响程序正确性和预期行为的情况下替代父类。</span></p>
<p class="md-end-block md-p"><span class="md-plain">下面是一个使用C#示例来说明里式替换原则的应用:</span></p>
<p class="md-end-block md-p"><span class="md-plain">假设我们正在开发一个图形绘制应用程序,其中包含多种形状(如圆形、矩形等)。我们希望能够根据用户选择的形状类型进行绘制操作。</span></p>
<p class="md-end-block md-p"><span class="md-plain">首先,定义一个抽象基类 </span><span class="md-pair-s" spellcheck="false"><code>Shape</code></span><span class="md-plain"> 来表示所有形状对象,并声明一个抽象方法 </span><span class="md-pair-s" spellcheck="false"><code>Draw</code></span><span class="md-plain"> 用于绘制该形状:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public abstract class Shape
{
public abstract void Draw();
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">然后,创建具体的子类来表示不同的形状。例如,创建 </span><span class="md-pair-s" spellcheck="false"><code>Circle</code></span><span class="md-plain"> 类和 </span><span class="md-pair-s" spellcheck="false"><code>Rectangle</code></span><span class="md-plain"> 类分别表示圆形和矩形,并实现它们自己特定的绘制逻辑:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle...");
}
}
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle...");
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">在这个例子中,每个具体的子类都可以替代其基类 </span><span class="md-pair-s" spellcheck="false"><code>Shape</code></span><span class="md-plain"> 并实现自己特定的绘制逻辑。这符合里式替换原则,因为无论是 </span><span class="md-pair-s" spellcheck="false"><code>Circle</code></span><span class="md-plain"> 还是 </span><span class="md-pair-s" spellcheck="false"><code>Rectangle</code></span><span class="md-plain"> 都可以在不破坏程序正确性和预期行为的情况下替代 </span><span class="md-pair-s" spellcheck="false"><code>Shape</code></span><span class="md-plain">。</span></p>
<p class="md-end-block md-p"><span class="md-plain">使用示例:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>Shape circle = new Circle();
circle.Draw();// 输出 "Drawing a circle..."
Shape rectangle = new Rectangle();
rectangle.Draw();// 输出 "Drawing a rectangle..."</code></pre>
<p class="md-end-block md-p"><span class="md-plain">通过将具体的子类对象赋值给基类引用变量,并调用相同的方法,我们可以看到不同形状的绘制操作被正确地执行。这证明了里式替换原则的有效性。</span></p>
<p class="md-end-block md-p"><span class="md-plain">总结:里式替换原则要求子类型必须能够替代其基类型,并且不会破坏程序正确性。在C#中,我们可以通过创建具体的子类来表示不同形状,并确保它们能够在继承自抽象基类时正确地实现自己特定的行为。这样可以提高代码的可扩展性和灵活性,并使得代码更易于维护和重用。</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">依赖倒置</span></h2>
<p class="md-end-block md-p"><span class="md-plain">依赖倒置原则(Dependency Inversion Principle,DIP)要求高层模块不应该依赖于低层模块的具体实现,而是应该依赖于抽象。同时,抽象不应该依赖于具体实现细节,而是应该由高层模块定义。</span></p>
<p class="md-end-block md-p"><span class="md-plain">下面是一个使用C#示例来说明依赖倒置原则的应用:</span></p>
<p class="md-end-block md-p"><span class="md-plain">假设我们正在开发一个电子商务系统,其中包含订单处理和支付功能。我们希望能够根据用户选择的支付方式进行订单支付操作。</span></p>
<p class="md-end-block md-p"><span class="md-plain">首先,定义一个抽象接口 </span><span class="md-pair-s" spellcheck="false"><code>IPaymentProcessor</code></span><span class="md-plain"> 来表示支付处理器,并声明一个方法 </span><span class="md-pair-s" spellcheck="false"><code>ProcessPayment</code></span><span class="md-plain"> 用于执行订单支付:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public interface IPaymentProcessor
{
void ProcessPayment(decimal amount);
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">然后,在具体的实现类中分别实现不同的支付方式。例如,创建 </span><span class="md-pair-s" spellcheck="false"><code>CreditCardPaymentProcessor</code></span><span class="md-plain"> 类和 </span><span class="md-pair-s" spellcheck="false"><code>PayPalPaymentProcessor</code></span><span class="md-plain"> 类分别表示信用卡和PayPal支付,并实现它们自己特定的支付逻辑:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class CreditCardPaymentProcessor : IPaymentProcessor
{
public void ProcessPayment(decimal amount)
{
Console.WriteLine($"Processing credit card payment of {amount} dollars...");
// 具体信用卡支付逻辑...
}
}
public class PayPalPaymentProcessor : IPaymentProcessor
{
public void ProcessPayment(decimal amount)
{
Console.WriteLine($"Processing PayPal payment of {amount} dollars...");
// 具体PayPal支付逻辑...
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">在这个例子中,每个具体的支付处理器都实现了 </span><span class="md-pair-s" spellcheck="false"><code>IPaymentProcessor</code></span><span class="md-plain"> 接口,并提供了自己特定的支付逻辑。这样,高层模块(订单处理模块)就可以依赖于抽象接口 </span><span class="md-pair-s" spellcheck="false"><code>IPaymentProcessor</code></span><span class="md-plain"> 而不是具体的实现类。</span></p>
<p class="md-end-block md-p"><span class="md-plain">使用示例:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class OrderProcessor
{
private IPaymentProcessor paymentProcessor;
public OrderProcessor(IPaymentProcessor paymentProcessor)
{
this.paymentProcessor = paymentProcessor;
}
public void ProcessOrder(decimal amount)
{
// 处理订单逻辑...
// 使用依赖注入的方式调用支付处理器
paymentProcessor.ProcessPayment(amount);
// 其他订单处理逻辑...
}
}
// 在应用程序中配置和使用不同的支付方式
var creditCardPayment = new CreditCardPaymentProcessor();
var payPalPayment = new PayPalPaymentProces </code></pre>
<h2 class="md-end-block md-heading"><span class="md-plain">接口隔离</span></h2>
<p class="md-end-block md-p"><span class="md-plain">接口隔离原则(Interface Segregation Principle,ISP)要求客户端不应该依赖于它们不使用的接口。一个类应该只依赖于它需要的接口,而不是依赖于多余的接口。</span></p>
<p class="md-end-block md-p"><span class="md-plain">下面是一个使用C#示例来说明接口隔离原则的应用:</span></p>
<p class="md-end-block md-p"><span class="md-plain">假设我们正在开发一个文件管理系统,其中包含文件上传和文件下载功能。我们希望能够根据用户需求提供相应的功能。</span></p>
<p class="md-end-block md-p"><span class="md-plain">首先,定义两个接口 </span><span class="md-pair-s" spellcheck="false"><code>IFileUploadable</code></span><span class="md-plain"> 和 </span><span class="md-pair-s" spellcheck="false"><code>IFileDownloadable</code></span><span class="md-plain"> 来表示文件上传和文件下载功能,并分别声明相应的方法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public interface IFileUploadable
{
void UploadFile(string filePath);
}
public interface IFileDownloadable
{
void DownloadFile(string fileId);
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">然后,在具体的实现类中分别实现这两个功能。例如,创建 </span><span class="md-pair-s" spellcheck="false"><code>LocalFileManager</code></span><span class="md-plain"> 类来处理本地文件操作,并实现对应的方法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class LocalFileManager : IFileUploadable, IFileDownloadable
{
public void UploadFile(string filePath)
{
Console.WriteLine($"Uploading file from local path: {filePath}");
// 具体本地上传逻辑...
}
public void DownloadFile(string fileId)
{
Console.WriteLine($"Downloading file with ID: {fileId} to local path");
// 具体本地下载逻辑...
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">在这个例子中,每个具体的实现类只关注自己需要用到的接口方法,而不需要实现多余的方法。这符合接口隔离原则,因为客户端可以根据需要依赖于相应的接口。</span></p>
<p class="md-end-block md-p"><span class="md-plain">使用示例:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class FileManagerClient
{
private IFileUploadable fileUploader;
private IFileDownloadable fileDownloader;
public FileManagerClient(IFileUploadable fileUploader, IFileDownloadable fileDownloader)
{
this.fileUploader = fileUploader;
this.fileDownloader = fileDownloader;
}
public void UploadAndDownloadFiles(string filePath, string fileId)
{
// 使用文件上传功能
fileUploader.UploadFile(filePath);
// 使用文件下载功能
fileDownloader.DownloadFile(fileId);
// 其他操作...
}
}
// 在应用程序中配置和使用具体的文件管理类
var localFileManager = new LocalFileManager();
var client = new FileManagerClient(localFileManager, localFileManager);
client.UploadAndDownloadFiles("path/to/file", "123456");</code></pre>
<p class="md-end-block md-p"><span class="md-plain">通过依赖注入的方式,我们可以将具体的实现类传递给客户端,并根据需要调用相应的接口方法。这样就遵循了接口隔离原则,使得客户端只依赖于它们所需的接口,并且不会受到多余方法的影响。这提高了代码的可维护性和灵活性,并促进了代码重用和扩展。</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">迪米特</span></h2>
<p class="md-end-block md-p"><span class="md-plain">迪米特法则(Law of Demeter,LoD),也称为最少知识原则(Principle of Least Knowledge),要求一个对象应该对其他对象有尽可能少的了解。一个类不应该直接与其他类耦合,而是通过中间类进行通信。</span></p>
<p class="md-end-block md-p"><span class="md-plain">下面是一个使用C#示例来说明迪米特法则的应用:</span></p>
<p class="md-end-block md-p"><span class="md-plain">假设我们正在开发一个社交网络系统,其中包含用户、好友和消息等功能。我们希望能够实现用户发送消息给好友的功能。</span></p>
<p class="md-end-block md-p"><span class="md-plain">首先,定义三个类 </span><span class="md-pair-s" spellcheck="false"><code>User</code></span><span class="md-plain">、</span><span class="md-pair-s" spellcheck="false"><code>Friend</code></span><span class="md-plain"> 和 </span><span class="md-pair-s" spellcheck="false"><code>Message</code></span><span class="md-plain"> 来表示用户、好友和消息,并在 </span><span class="md-pair-s" spellcheck="false"><code>User</code></span><span class="md-plain"> 类中实现发送消息的方法:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class User
{
private string name;
private List<Friend> friends;
public User(string name)
{
this.name = name;
this.friends = new List<Friend>();
}
public void AddFriend(Friend friend)
{
friends.Add(friend);
}
public void SendMessageToFriends(string messageContent)
{
Message message = new Message(messageContent);
foreach (Friend friend in friends)
{
friend.ReceiveMessage(message);
}
Console.WriteLine($"User {name} sent a message to all friends.");
}
}
public class Friend
{
private string name;
public Friend(string name)
{
this.name = name;
}
public void ReceiveMessage(Message message)
{
Console.WriteLine($"Friend {name} received a message: {message.Content}");
// 处理接收到的消息...
}
}
public class Message
{
public string Content { get; set; }
public Message(string content)
{
Content = content;
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">在这个例子中,</span><span class="md-pair-s" spellcheck="false"><code>User</code></span><span class="md-plain"> 类表示用户,</span><span class="md-pair-s" spellcheck="false"><code>Friend</code></span><span class="md-plain"> 类表示好友,</span><span class="md-pair-s" spellcheck="false"><code>Message</code></span><span class="md-plain"> 类表示消息。用户可以添加好友,并通过 </span><span class="md-pair-s" spellcheck="false"><code>SendMessageToFriends</code></span><span class="md-plain"> 方法向所有好友发送消息。</span></p>
<p class="md-end-block md-p"><span class="md-plain">使用示例:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>User user1 = new User("Alice");
User user2 = new User("Bob");
Friend friend1 = new Friend("Charlie");
Friend friend2 = new Friend("David");
user1.AddFriend(friend1);
user2.AddFriend(friend2);
user1.SendMessageToFriends("Hello, friends!");</code></pre>
<p class="md-end-block md-p"><span class="md-plain">在这个示例中,用户对象只与好友对象进行通信,并不直接与消息对象进行通信。这符合迪米特法则的要求,即一个对象应该尽可能少地了解其他对象。</span></p>
<p class="md-end-block md-p"><span class="md-plain">通过将消息发送的责任委托给好友对象,在用户类中只需要调用 </span><span class="md-pair-s" spellcheck="false"><code>friend.ReceiveMessage(message)</code></span><span class="md-plain"> 方法来发送消息给所有好友。这样可以降低类之间的耦合性,并提高代码的可维护性和灵活性。</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">合成复用</span></h2>
<p class="md-end-block md-p"><span class="md-plain">合成复用原则(Composite Reuse Principle,CRP)要求尽量使用对象组合,而不是继承来达到复用的目的。通过将现有对象组合起来创建新的对象,可以更灵活地实现功能的复用和扩展。</span></p>
<p class="md-end-block md-p"><span class="md-plain">下面是一个使用C#示例来说明合成复用原则的应用:</span></p>
<p class="md-end-block md-p"><span class="md-plain">假设我们正在开发一个图形库,其中包含各种形状(如圆形、矩形等)。我们希望能够实现一个可以绘制多个形状的画板。</span></p>
<p class="md-end-block md-p"><span class="md-plain">首先,定义一个抽象基类 </span><span class="md-pair-s" spellcheck="false"><code>Shape</code></span><span class="md-plain"> 来表示图形,并声明抽象方法 </span><span class="md-pair-s" spellcheck="false"><code>Draw</code></span><span class="md-plain">:</span></p>
<pre class="md-fences mock-cm md-end-block highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public abstract class Shape
{
public abstract void Draw();
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">然后,在具体的子类中分别实现各种形状。例如,创建 </span><span class="md-pair-s" spellcheck="false"><code>Circle</code></span><span class="md-plain"> 类和 </span><span class="md-pair-s" spellcheck="false"><code>Rectangle</code></span><span class="md-plain"> 类来表示圆形和矩形,并重写父类中的 </span><span class="md-pair-s" spellcheck="false"><code>Draw</code></span><span class="md-plain"> 方法:</span></p>
<pre class="md-fences mock-cm md-end-block highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
// 具体绘制圆形逻辑...
}
}
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle");
// 具体绘制矩形逻辑...
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">接下来,创建一个画板类 </span><span class="md-pair-s" spellcheck="false"><code>Canvas</code></span><span class="md-plain"> 来管理并绘制多个图形。在该类中使用对象组合将多个图形组合在一起:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>public class Canvas
{
private List<Shape> shapes;
public Canvas()
{
shapes = new List<Shape>();
}
public void AddShape(Shape shape)
{
shapes.Add(shape);
}
public void DrawShapes()
{
foreach (Shape shape in shapes)
{
shape.Draw();
}
Console.WriteLine("All shapes are drawn.");
}
}</code></pre>
<p class="md-end-block md-p"><span class="md-plain">在这个例子中,</span><span class="md-pair-s" spellcheck="false"><code>Canvas</code></span><span class="md-plain"> 类通过对象组合的方式将多个图形对象组合在一起,并提供了添加图形和绘制图形的方法。</span></p>
<p class="md-end-block md-p"><span class="md-plain">使用示例:</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded highlighter-prismjs language-none prismjs-lines-highlighted" lang="csharp" tabindex="0" spellcheck="false"><code>Canvas canvas = new Canvas();
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
canvas.AddShape(circle);
canvas.AddShape(rectangle);
canvas.DrawShapes();</code></pre>
<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">在这个示例中,我们创建了一个画板对象 </span><span class="md-pair-s" spellcheck="false"><code>canvas</code></span><span class="md-plain">,并向其中添加了一个圆形和一个矩形。然后调用 </span><span class="md-pair-s" spellcheck="false"><code>DrawShapes</code></span><span class="md-plain"> 方法来绘制所有的图形。</span></p>
<p class="md-end-block md-p"><span class="md-plain md-expand">通过使用对象组合而不是继承,我们可以更灵活地实现功能的复用和扩展。例如,可以轻松地添加新的图形类型或修改现有图形类型的行为,而不会影响到画板类。这符合合成复用原则,并提高了代码的可维护性和灵活性。</span></p>
</div>
<div id="MySignature" role="contentinfo">
<p><img src="https://img2020.cnblogs.com/blog/539967/202101/539967-20210113232527609-784083062.png"></p>
<p>付费内容,请联系本人QQ:1002453261</p>
<p>本文来自博客园,作者:明志德道,转载请注明原文链接:https://www.cnblogs.com/for-easy-fast/p/17762706.html</p><br><br>
来源:https://www.cnblogs.com/for-easy-fast/p/17762706.html
頁:
[1]