C#代码重构技巧与设计模式应用场景
C#代码重构技巧
提炼方法(Extract Method)
在C#编程中,提炼方法是一种极为基础且常用的重构技巧。当一个方法变得冗长、复杂,包含多个不同职责的代码块时,将这些代码块分别提炼成独立的方法,能显著提高代码的可读性和可维护性。
假设我们有如下一段计算订单总价并打印订单信息的代码:
public class Order
{
public List<Product> Products { get; set; }
public double CalculateTotalPrice()
{
double total = 0;
foreach (var product in Products)
{
total += product.Price * product.Quantity;
}
Console.WriteLine("Order details:");
foreach (var product in Products)
{
Console.WriteLine($"Product: {product.Name}, Price: {product.Price}, Quantity: {product.Quantity}");
}
Console.WriteLine($"Total Price: {total}");
return total;
}
}
public class Product
{
public string Name { get; set; }
public double Price { get; set; }
public int Quantity { get; set; }
}
在CalculateTotalPrice
方法中,既包含了计算总价的逻辑,又包含了打印订单信息的逻辑。我们可以将计算总价和打印订单信息的逻辑分别提炼成独立的方法:
public class Order
{
public List<Product> Products { get; set; }
public double CalculateTotalPrice()
{
double total = CalculateTotal();
PrintOrderDetails(total);
return total;
}
private double CalculateTotal()
{
double total = 0;
foreach (var product in Products)
{
total += product.Price * product.Quantity;
}
return total;
}
private void PrintOrderDetails(double total)
{
Console.WriteLine("Order details:");
foreach (var product in Products)
{
Console.WriteLine($"Product: {product.Name}, Price: {product.Price}, Quantity: {product.Quantity}");
}
Console.WriteLine($"Total Price: {total}");
}
}
public class Product
{
public string Name { get; set; }
public double Price { get; set; }
public int Quantity { get; set; }
}
这样,CalculateTotalPrice
方法变得更加清晰,每个提炼出来的方法职责单一,便于理解和维护。
内联方法(Inline Method)
与提炼方法相反,当一个方法的逻辑过于简单,其存在反而增加了代码的复杂性和阅读成本时,可以使用内联方法重构技巧。例如,有这样一个简单的方法:
public class MathUtils
{
public int Add(int a, int b)
{
return a + b;
}
}
如果在调用处,Add
方法的使用场景非常简单,比如:
MathUtils utils = new MathUtils();
int result = utils.Add(3, 5);
此时可以将Add
方法内联,直接写成:
int result = 3 + 5;
这样可以减少方法调用的开销,并且使代码更加直接明了。但需要注意的是,如果该方法在多个地方被调用,内联可能会导致代码重复,需要权衡利弊。
替换临时变量(Replace Temp with Query)
在C#代码中,临时变量常常用于存储中间计算结果。然而,过多的临时变量会使代码变得混乱,尤其是当它们在方法中被多次赋值时。通过替换临时变量为查询方法,可以使代码更加清晰。
例如,我们有如下计算产品折扣后价格的代码:
public class Product
{
public double Price { get; set; }
public double Discount { get; set; }
public double CalculateDiscountedPrice()
{
double discountAmount = Price * Discount;
double discountedPrice = Price - discountAmount;
return discountedPrice;
}
}
可以将临时变量替换为查询方法:
public class Product
{
public double Price { get; set; }
public double Discount { get; set; }
public double CalculateDiscountedPrice()
{
return Price - CalculateDiscountAmount();
}
private double CalculateDiscountAmount()
{
return Price * Discount;
}
}
这样,CalculateDiscountedPrice
方法的逻辑更加清晰,并且CalculateDiscountAmount
方法可以在其他需要计算折扣金额的地方复用。
移除死代码(Remove Dead Code)
死代码是指永远不会被执行的代码,可能是因为条件永远不成立,或者是方法不再被调用等原因。移除死代码能使代码库更加简洁,减少维护负担。
例如,有如下代码:
public class SomeClass
{
public void SomeMethod()
{
bool flag = false;
if (flag)
{
Console.WriteLine("This is dead code");
}
// 下面这个方法已经不再被其他地方调用
UnusedMethod();
}
private void UnusedMethod()
{
Console.WriteLine("This method is not used anymore");
}
}
我们应该删除if (flag)
块内的代码以及UnusedMethod
方法,使代码更加简洁:
public class SomeClass
{
public void SomeMethod()
{
// 代码变得简洁,无死代码
}
}
重构条件逻辑(Refactoring Conditional Logic)
复杂的条件逻辑会使代码难以理解和维护。可以通过多种方式重构条件逻辑,使其更加清晰。
分解条件表达式(Decompose Conditional)
当一个条件表达式包含多个逻辑判断,并且每个判断都有不同的处理逻辑时,可以将其分解为多个简单的条件表达式。
例如,有如下代码:
public class OrderProcessor
{
public void ProcessOrder(Order order)
{
if (order.IsPriority && order.TotalPrice > 100 && order.Products.Count > 5)
{
Console.WriteLine("Process as high - priority order");
// 高优先级订单处理逻辑
}
else
{
Console.WriteLine("Process as normal order");
// 普通订单处理逻辑
}
}
}
可以分解为:
public class OrderProcessor
{
public void ProcessOrder(Order order)
{
if (IsHighPriorityOrder(order))
{
Console.WriteLine("Process as high - priority order");
// 高优先级订单处理逻辑
}
else
{
Console.WriteLine("Process as normal order");
// 普通订单处理逻辑
}
}
private bool IsHighPriorityOrder(Order order)
{
return order.IsPriority && order.TotalPrice > 100 && order.Products.Count > 5;
}
}
这样,ProcessOrder
方法的逻辑更加清晰,IsHighPriorityOrder
方法也可以在其他需要判断高优先级订单的地方复用。
以多态取代条件表达式(Replace Conditional with Polymorphism)
当条件表达式根据对象的类型来执行不同的逻辑时,可以使用多态来代替条件表达式,以实现更加灵活和可维护的代码。
假设有一个根据不同形状计算面积的程序:
public class Shape
{
public enum ShapeType { Circle, Rectangle }
public ShapeType Type { get; set; }
public double Radius { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double CalculateArea()
{
if (Type == ShapeType.Circle)
{
return Math.PI * Radius * Radius;
}
else if (Type == ShapeType.Rectangle)
{
return Width * Height;
}
return 0;
}
}
可以通过多态重构为:
public abstract class Shape
{
public abstract double CalculateArea();
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double CalculateArea()
{
return Width * Height;
}
}
这样,在使用时,只需要根据实际的形状类型调用CalculateArea
方法,而不需要复杂的条件判断:
Shape circle = new Circle { Radius = 5 };
double circleArea = circle.CalculateArea();
Shape rectangle = new Rectangle { Width = 4, Height = 6 };
double rectangleArea = rectangle.CalculateArea();
设计模式应用场景
单例模式(Singleton Pattern)
单例模式确保一个类只有一个实例,并提供一个全局访问点。在C#中,单例模式常用于需要全局唯一实例的场景,比如数据库连接池、日志记录器等。
实现方式
- 饿汉式单例:在类加载时就创建实例。
public sealed class Logger
{
private static readonly Logger instance = new Logger();
private Logger() { }
public static Logger Instance
{
get
{
return instance;
}
}
public void LogMessage(string message)
{
Console.WriteLine($"Logging: {message}");
}
}
- 懒汉式单例:在第一次使用时创建实例。
public sealed class DatabaseConnectionPool
{
private static DatabaseConnectionPool instance;
private static readonly object padlock = new object();
private DatabaseConnectionPool() { }
public static DatabaseConnectionPool Instance
{
get
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new DatabaseConnectionPool();
}
}
}
return instance;
}
}
// 数据库连接池相关方法
}
应用场景
- 数据库连接池:在应用程序中,数据库连接是宝贵的资源。使用单例模式创建数据库连接池,可以确保整个应用程序共享一个连接池,避免频繁创建和销毁连接带来的性能开销。
- 日志记录器:在整个应用程序中,可能需要一个统一的日志记录器来记录各种事件。单例模式的日志记录器可以保证所有的日志都被记录到同一个地方,便于管理和分析。
工厂模式(Factory Pattern)
工厂模式提供了一种创建对象的方式,将对象的创建和使用分离。在C#中,工厂模式常用于创建对象的过程比较复杂,或者需要根据不同条件创建不同类型对象的场景。
简单工厂模式(Simple Factory Pattern)
简单工厂模式定义了一个工厂类,用于创建产品对象。
假设有一个创建不同类型汽车的场景:
public abstract class Car
{
public abstract void Drive();
}
public class Sedan : Car
{
public override void Drive()
{
Console.WriteLine("Driving a sedan");
}
}
public class SUV : Car
{
public override void Drive()
{
Console.WriteLine("Driving an SUV");
}
}
public class CarFactory
{
public static Car CreateCar(string carType)
{
switch (carType.ToLower())
{
case "sedan":
return new Sedan();
case "suv":
return new SUV();
default:
return null;
}
}
}
使用时:
Car sedan = CarFactory.CreateCar("sedan");
sedan.Drive();
工厂方法模式(Factory Method Pattern)
工厂方法模式将对象的创建延迟到子类。
public abstract class CarFactory
{
public abstract Car CreateCar();
}
public class SedanFactory : CarFactory
{
public override Car CreateCar()
{
return new Sedan();
}
}
public class SUVFactory : CarFactory
{
public override Car CreateCar()
{
return new SUV();
}
}
使用时:
CarFactory sedanFactory = new SedanFactory();
Car sedan = sedanFactory.CreateCar();
sedan.Drive();
抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
假设汽车除了本身,还需要创建相应的轮胎:
public abstract class Car
{
public abstract void Drive();
}
public class Sedan : Car
{
public override void Drive()
{
Console.WriteLine("Driving a sedan");
}
}
public class SUV : Car
{
public override void Drive()
{
Console.WriteLine("Driving an SUV");
}
}
public abstract class Tire
{
public abstract void Roll();
}
public class SedanTire : Tire
{
public override void Roll()
{
Console.WriteLine("Sedan tire is rolling");
}
}
public class SUVTire : Tire
{
public override void Roll()
{
Console.WriteLine("SUV tire is rolling");
}
}
public abstract class CarFactory
{
public abstract Car CreateCar();
public abstract Tire CreateTire();
}
public class SedanFactory : CarFactory
{
public override Car CreateCar()
{
return new Sedan();
}
public override Tire CreateTire()
{
return new SedanTire();
}
}
public class SUVFactory : CarFactory
{
public override Car CreateCar()
{
return new SUV();
}
public override Tire CreateTire()
{
return new SUVTire();
}
}
使用时:
CarFactory sedanFactory = new SedanFactory();
Car sedan = sedanFactory.CreateCar();
Tire sedanTire = sedanFactory.CreateTire();
sedan.Drive();
sedanTire.Roll();
应用场景
- 对象创建复杂:当创建对象需要进行复杂的初始化操作,如从配置文件读取参数、进行数据库查询等,使用工厂模式可以将这些复杂的操作封装在工厂类中,使客户端代码更加简洁。
- 根据不同条件创建不同类型对象:在游戏开发中,根据玩家的选择创建不同类型的角色,或者在图形绘制中,根据用户选择绘制不同的图形,都可以使用工厂模式。
策略模式(Strategy Pattern)
策略模式定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。在C#中,策略模式常用于需要根据不同情况选择不同算法或行为的场景。
假设有一个计算订单运费的场景,根据不同的运输方式有不同的运费计算策略:
public interface IShippingStrategy
{
double CalculateShippingCost(double orderTotal);
}
public class ExpressShipping : IShippingStrategy
{
public double CalculateShippingCost(double orderTotal)
{
if (orderTotal > 100)
{
return 10;
}
return 20;
}
}
public class StandardShipping : IShippingStrategy
{
public double CalculateShippingCost(double orderTotal)
{
if (orderTotal > 50)
{
return 5;
}
return 10;
}
}
public class Order
{
public double TotalPrice { get; set; }
public IShippingStrategy ShippingStrategy { get; set; }
public double CalculateShippingCost()
{
return ShippingStrategy.CalculateShippingCost(TotalPrice);
}
}
使用时:
Order order = new Order { TotalPrice = 120 };
order.ShippingStrategy = new ExpressShipping();
double shippingCost = order.CalculateShippingCost();
应用场景
- 算法切换:在图像处理中,可能有不同的图像压缩算法,如JPEG、PNG等。使用策略模式可以根据用户需求或图像特点动态切换压缩算法。
- 行为定制:在电商系统中,不同的促销活动可能有不同的折扣计算策略,使用策略模式可以方便地实现这些不同策略的切换和定制。
观察者模式(Observer Pattern)
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会通知所有观察者对象,使它们能够自动更新。在C#中,观察者模式常用于实现事件驱动的系统。
假设有一个股票价格变化通知的场景:
using System;
using System.Collections.Generic;
public class Stock
{
private string symbol;
private double price;
private List<IInvestor> investors = new List<IInvestor>();
public Stock(string symbol, double price)
{
this.symbol = symbol;
this.price = price;
}
public double Price
{
get { return price; }
set
{
if (price != value)
{
price = value;
NotifyInvestors();
}
}
}
public void Attach(IInvestor investor)
{
investors.Add(investor);
}
public void Detach(IInvestor investor)
{
investors.Remove(investor);
}
private void NotifyInvestors()
{
foreach (var investor in investors)
{
investor.Update(this);
}
}
}
public interface IInvestor
{
void Update(Stock stock);
}
public class Investor : IInvestor
{
private string name;
public Investor(string name)
{
this.name = name;
}
public void Update(Stock stock)
{
Console.WriteLine($"Notification to {name}: {stock.symbol} price has changed to {stock.Price}");
}
}
使用时:
Stock stock = new Stock("ABC", 50);
Investor investor1 = new Investor("John");
Investor investor2 = new Investor("Jane");
stock.Attach(investor1);
stock.Attach(investor2);
stock.Price = 55;
应用场景
- 事件通知:在图形用户界面(GUI)开发中,当用户点击按钮、移动滑块等操作时,需要通知相关的组件进行更新,这可以通过观察者模式实现。
- 消息推送:在即时通讯系统中,当有新消息到达时,需要推送给所有在线的用户,观察者模式可以很好地实现这一功能。
装饰器模式(Decorator Pattern)
装饰器模式允许向一个现有的对象添加新的功能,同时又不改变其结构。在C#中,装饰器模式常用于在运行时给对象添加功能的场景。
假设有一个绘制图形的场景,我们可以给基本图形添加边框、阴影等装饰:
public abstract class Shape
{
public abstract void Draw();
}
public class Rectangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a rectangle");
}
}
public abstract class ShapeDecorator : Shape
{
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape)
{
this.decoratedShape = decoratedShape;
}
public override void Draw()
{
decoratedShape.Draw();
}
}
public class BorderDecorator : ShapeDecorator
{
public BorderDecorator(Shape decoratedShape) : base(decoratedShape) { }
public override void Draw()
{
Console.WriteLine("Adding border");
base.Draw();
Console.WriteLine("Border added");
}
}
public class ShadowDecorator : ShapeDecorator
{
public ShadowDecorator(Shape decoratedShape) : base(decoratedShape) { }
public override void Draw()
{
Console.WriteLine("Adding shadow");
base.Draw();
Console.WriteLine("Shadow added");
}
}
使用时:
Shape rectangle = new Rectangle();
Shape borderedRectangle = new BorderDecorator(rectangle);
Shape borderedAndShadowedRectangle = new ShadowDecorator(borderedRectangle);
borderedAndShadowedRectangle.Draw();
应用场景
- 动态添加功能:在游戏开发中,角色可能在游戏过程中获得新的技能或装备,这些新的能力可以看作是对角色的装饰。使用装饰器模式可以方便地为角色动态添加这些功能。
- 功能组合:在文本处理中,可能需要对文本同时应用加粗、斜体、下划线等多种格式,装饰器模式可以实现这些格式的灵活组合。