You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中创建对象及嵌套子对象的适用设计模式咨询

形状/构件创建逻辑重构方案

你一开始猜测的工厂方法模式可以用,但不是核心——你这个场景最适配的是模板方法模式,工厂方法只需要用来做不同类型创建器的分发即可。

为什么选模板方法

你所有的创建逻辑流程骨架是完全固定的:

  • 第一步:创建Extrusion拉伸体
  • 第二步:对拉伸体做自定义配置、按需创建孔洞
  • 第三步:创建Loop轮廓环
  • 第四步:循环创建Vertices顶点

不同形状、不同Foundation构件的区别,仅仅是每个步骤传入的参数、是否需要额外创建子构件(比如Pedestal要附带创建Grout),完全是模板方法的典型适用场景:把固定的流程骨架固化在基类,差异逻辑拆到具体实现类里,从根上解决你现在单方法堆if-else、重复代码满天飞的问题。

现有代码的核心问题

先列下当前代码的硬伤,重构就是针对性解决这些问题:

  • 一个方法塞了所有类型的创建逻辑,if-else分支堆了几百行,改一个类型的逻辑容易误碰其他类型的代码,加新类型必须改这个老方法,违反开闭原则
  • 重复代码太多:CreateLoop、遍历DataRow创建Vertex的逻辑在每个分支里重复编写,只是参数不同
  • 变量乱复用:objectiveElement在分支里反复用临时变量存储、覆盖,稍不注意就会把元素挂错父节点
  • 通用逻辑和业务配置混在一起,找某类构件的专属规则要翻半天

具体重构步骤

1. 封装公共上下文

先把你CreateFoundation方法里传的一长串参数封装成一个上下文对象,避免后面方法参数列表太长:

// 所有创建流程共用的参数都放这里
public class FoundationCreateContext
{
    public FoundationType FoundationType { get; set; }
    public FoundationColumnName HeightColumnName { get; set; }
    public DbElement HostElement { get; set; }
    public DataSet SourceDataSet { get; set; }
    public DataTable SourceDataTable { get; set; }
    public DataRow HeightDataRow { get; set; }
    public string ExtrusionName { get; set; }
    public string Note { get; set; }
    public Guid GroupId { get; set; }
}

2. 定义抽象基类,固化创建流程

基类里把固定的创建流程写死,只把有差异的步骤定义成抽象方法或者虚方法,交给具体子类实现:

public abstract class FoundationCreatorBase
{
    protected readonly FoundationCreateContext _ctx;
    // 基类构造传入上下文
    protected FoundationCreatorBase(FoundationCreateContext ctx) => _ctx = ctx;

    // 模板方法:固定创建流程,不允许子类重写改流程
    public void ExecuteCreate()
    {
        var extrusion = CreateExtrusion();
        ConfigExtrusionAndCreateHoles(extrusion);
        var loop = CreateLoop(extrusion);
        CreateVertices(loop);
        CreateExtraChildComponents();
    }

    // ----- 差异步骤:子类必须/可以重写 -----
    // 创建拉伸体:每个类型参数不一样,子类必须实现
    protected abstract DbElement CreateExtrusion();
    // 拉伸体创建后的配置、打孔:默认空实现,子类需要就重写
    protected virtual void ConfigExtrusionAndCreateHoles(DbElement extrusion) { }
    // 创建轮廓环:逻辑通用,直接在基类实现,子类有特殊需求再重写
    protected virtual DbElement CreateLoop(DbElement extrusion)
    {
        return _ctx.HostElement.CreateLoop(extrusion, Guid.NewGuid().ToString());
    }
    // 创建顶点:每个类型取的坐标字段不一样,子类必须实现
    protected abstract void CreateVertices(DbElement loop);
    // 创建额外子构件(比如Pedestal需要创建Grout):默认空实现
    protected virtual void CreateExtraChildComponents() { }

    // ----- 通用工具方法,所有子类共用,不用重复写 -----
    protected double ParseToMm(string value) => StringToDouble(value) * 1000d;
    protected double GetBaseZ() => ParseToMm(_ctx.SourceDataTable.Rows[0]["ZPOINT"].ToString());
}

3. 每个构件类型单独写实现类

举个最简单的TieGirder实现类例子,逻辑非常清晰:

public class TieGirderFoundationCreator : FoundationCreatorBase
{
    public TieGirderFoundationCreator(FoundationCreateContext ctx) : base(ctx) { }

    protected override DbElement CreateExtrusion()
    {
        double height = StringToDouble(_ctx.SourceDataTable.Rows[0]["TIEGIRDER_HEIGHT"].ToString());
        string noteDim = _ctx.SourceDataSet.Tables[TableName.CONVERTER_TIEGIRDER_DATA.ToString()].Rows[0][FoundationColumnName.NOTE_DIM.ToString()].ToString();
        
        return _ctx.HostElement.CreateExtrusion(
            _ctx.FoundationType,
            _ctx.HostElement,
            GetBaseZ(),
            height,
            _ctx.ExtrusionName,
            _ctx.GroupId,
            _ctx.Note,
            noteDim
        );
    }

    protected override void CreateVertices(DbElement loop)
    {
        foreach (DataRow dr in _ctx.SourceDataTable.Rows)
        {
            _ctx.HostElement.CreateVertex(
                loop,
                Guid.NewGuid().ToString(),
                ParseToMm(dr["XPOINT"].ToString()),
                ParseToMm(dr["YPOINT"].ToString()),
                ParseToMm(dr["ZPOINT"].ToString())
            );
        }
    }
}

像Pedestal这种逻辑复杂、需要额外创建Grout的类型,单独写PedestalFoundationCreator类,在重写CreateExtraChildComponents方法的时候写Grout的创建逻辑就行,不用和其他类型的代码混在一起。你最开始贴的Circle、Rectangle、Pyramid这类普通形状,也可以用完全一样的思路,写个ShapeCreatorBase基类,每个形状单独做实现类。

4. 加个简单工厂做类型分发

用简单工厂根据传入的FoundationType返回对应的创建器实例,替代原来的超长if-else:

public static class FoundationCreatorFactory
{
    public static FoundationCreatorBase GetCreator(FoundationCreateContext ctx)
    {
        return ctx.FoundationType switch
        {
            FoundationType.FOOTING 
                or FoundationType.SCREEDTHK 
                or FoundationType.LEANCONCRETE 
                or FoundationType.CRUSHEDSTONES => new SimplePlateFoundationCreator(ctx),
            FoundationType.PEDESTAL => new PedestalFoundationCreator(ctx),
            FoundationType.PILE => new PileFoundationCreator(ctx),
            FoundationType.TIEGIRDER => new TieGirderFoundationCreator(ctx),
            _ => throw new NotSupportedException($"不支持的基础类型:{ctx.FoundationType}")
        };
    }
}

5. 简化原入口方法

原来几百行的CreateFoundation方法,最后可以简化成几行:

private void CreateFoundation(FoundationType foundationType, FoundationColumnName heightColumnName, DbElement objectiveElement, DataSet dsConverterData, DataTable dtConverterData, DataRow item_height, string extrusionName, string s3d_note, Guid PDMS_GROUP_ID)
{
    var context = new FoundationCreateContext
    {
        FoundationType = foundationType,
        HeightColumnName = heightColumnName,
        HostElement = objectiveElement,
        SourceDataSet = dsConverterData,
        SourceDataTable = dtConverterData,
        HeightDataRow = item_height,
        ExtrusionName = extrusionName,
        Note = s3d_note,
        GroupId = PDMS_GROUP_ID
    };
    var creator = FoundationCreatorFactory.GetCreator(context);
    creator.ExecuteCreate();
}

重构后的收益

  • 没有超长if-else了,每个类型的逻辑单独放一个文件,改哪个类型就找对应的类,不会碰坏其他逻辑
  • 通用代码全部抽到基类复用,不用再到处抄CreateLoop、坐标转换的代码
  • 流程在基类固化,不会出现某类构件漏写CreateLoop、漏加顶点的低级错误
  • 加新类型的时候,只要新增一个实现类,在工厂里加个分支就行,不用动原有流程代码,符合开闭原则
  • 像Pedestal里嵌套Grout创建的逻辑,可以在子实现类里复用基类的流程方法,不用重复写拉伸体、环、顶点的创建逻辑

内容的提问来源于stack exchange,提问作者seongmincho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 07:48:27