封装变化(Part Three)

本文探讨了通过策略模式实现排序算法的灵活性,并介绍了如何利用依赖注入来降低代码间的耦合度,以适应需求变化。

设想这样一个需求,我们需要为自己的框架提供一个负责排序的组件。目前需要实现的是冒泡排序算法和快速排序算法,根据“面向接口编程”的思想,我们可以为这些排序算法提供一个统一的接口ISort,在这个接口中有一个方法Sort(),它能接受一个object数组参数。对数组进行排序后,返回该数组。接口的定义如下:

public interface ISort
{
    void Sort(ref object[] beSorted);
}

其类图如下:

change05.gif

然而一般对于排序而言,排列是有顺序之分的,例如升序,或者降序,返回的结果也不相同。最简单的方法我们可以利用if语句来实现这一目的,例如在QuickSort类中:

public class QuickSort:ISort
{
    private string m_SortType;

    public QuickSort(string sortType)
    {
         m_SortType = sortType;
    }

    public void Sort(ref object[] beSorted)
    {
           if (m_SortType.ToUpper().Trim() == “ASCENDING”)
          {
              //执行升序的快速排序;
          }
          else
          {
              //执行降序的快速排序;
          }
     }
}

当然,我们也可以将string类型的SortType定义为枚举类型,减少出现错误的可能性。然而仔细阅读代码,我们可以发现这样的代码是非常僵化的,一旦需要扩展,如果要求我们增加新的排序顺序,例如字典顺序,那么我们面临的工作会非常繁重。也就是说,变化产生了。通过分析,我们发现所谓排序的顺序,恰恰是排序算法中最关键的一环,它决定了谁排列在前,谁排列在后。然而它并不属于排序算法,而是一种比较的策略,后者说是比较的行为。

如果仔细分析实现ISort接口的类,例如QuickSort类,它在实现排序算法的时候,需要对两个对象作比较。按照重构的做法,实质上我们可以在Sort方法中抽取出一个私有方法Compare(),通过返回的布尔值,决定哪个对象在前,哪个对象在后。显然,可能发生变化的是这个比较行为,利用“封装抽象”的原理,就应该为该行为建立一个专有的接口ICompare,然而分别定义实现升序、降序或者字典排序的类对象。

change06.gif

我们在每一个实现了ISort接口的类构造函数中,引入ICompare接口对象,从而建立起排序算法与比较算法的弱耦合关系(因为这个关系与抽象的ICompare接口相关),例如QuickSort类:

public class QuickSort:ISort
{
    private ICompare m_Compare;
    public QuickSort(ICompare compare)
    {
        m_Compare= compare;
    }
    public void Sort(ref object[] beSorted)
    {
        //实现略
        for (int i = 0; i < beSorted.Length - 1; i++)
       {
            if (m_Compare.Compare(beSorted[i],beSorted[i+1))
           {
               //略;
           }
        }
        //实现略
    }
}

最后的类图如下:

change07.gif

通过对比较策略的封装,以应对它的变化,显然是Stategy模式的设计。事实上,这里的排序算法也可能是变化的,例如实现二叉树排序。由于我们已经引入了“面向接口编程”的思想,我们完全可以轻易的添加一个新的类BinaryTreeSort,来实现ISort接口。对于调用方而言,ISort接口的实现,同样是一个Strategy模式。此时的类结构,完全是一个对扩展开发的状态,它完全能够适应类库调用者新需求的变化。

再以PetShop为例,在这个项目中涉及到订单的管理,例如插入订单。考虑到访问量的关系,PetShop为订单管理提供了同步和异步的方式。显然,在实际应用中只能使用这两种方式的其中一种,并由具体的应用环境所决定。那么为了应对这样一种可能会很频繁的变化,我们仍然需要利用“封装变化”的原理,建立抽象级别的对象,也就是IOrderStrategy接口:

public interface IOrderStrategy
{
    void Insert(PetShop.Model.OrderInfo order);
}

然后定义两个类OrderSynchronous和OrderAsynchronous。类结构如下:

change08.gif

在PetShop中,由于用户随时都可能会改变插入订单的策略,因此对于业务层的订单领域对象而言,不能与具体的订单策略对象产生耦合关系。也就是说,在领域对象Order类中,不能new一个具体的订单策略对象,如下面的代码:

IOrderStrategy orderInsertStrategy = new OrderSynchronous();

在Martin Fowler的文章《IoC容器和Dependency Injection模式》中,提出了解决这类问题的办法,他称之为依赖注入。不过由于PetShop并没有使用诸如Sping.Net等IoC容器,因此解决依赖问题,通常是利用配置文件结合反射来完成的。在领域对象Order类中,是这样实现的:

public class Order
{
    private static readonly IOrderStategy orderInsertStrategy = LoadInsertStrategy();
    private static IOrderStrategy LoadInsertStrategy()
    {
        // Look up which strategy to use from config file
        string path = ConfigurationManager.AppSettings[”OrderStrategyAssembly”];
        string className = ConfigurationManager.AppSettings[”OrderStrategyClass”];

        // Load the appropriate assembly and class
        return (IOrderStrategy)Assembly.Load(path).CreateInstance(className);
    }
}

在配置文件web.config中,配置如下的Section:
<add key=”OrderStrategyAssembly” value=”PetShop.BLL”/>
<add key=”OrderStrategyClass” value=”PetShop.BLL.OrderSynchronous”/>

这其实是一种折中的Service Locator模式。将定位并创建依赖对象的逻辑直接放到对象中,在PetShop的例子中,不失为一种好方法。毕竟在这个例子中,需要依赖注入的对象并不太多。但我们也可以认为是一种无奈的妥协的办法,一旦这种依赖注入的逻辑增多,为给程序者带来一定的麻烦,这时就需要一个专门的轻量级IoC容器了。

写到这里,似乎已经脱离了“封装变化”的主题。但事实上我们需要明白,利用抽象的方式封装变化,固然是应对需求变化的王道,但它也仅仅能解除调用者与被调用者相对的耦合关系,只要还涉及到具体对象的创建,即使引入了工厂模式,但具体的工厂对象的创建仍然是必不可少的。那么,对于这样一些业已被封装变化的对象,我们还应该充分利用“依赖注入”的方式来彻底解除两者之间的耦合。

转载于:https://www.cnblogs.com/wayne-ivan/archive/2006/09/07/496890.html

<think>我们面临的任务是将现有的Abaqus Python脚本改造成支持批量处理的版本。批量处理的参数包括: 1. 弹簧的stp文件路径(位于指定文件夹) 2. 弹簧平移量数据(存储在一个xlsx文件中) ### 解决方案思路 1. **读取xlsx文件中的平移量数据**:使用Python的`openpyxl`或`pandas`库读取xlsx文件,获取每个弹簧模型对应的平移量。 2. **遍历指定文件夹中的stp文件**:使用`os`模块列出文件夹中的所有stp文件。 3. **循环处理每个stp文件**:对每个stp文件,根据文件名或某种映射关系找到对应的平移量数据,然后调用原有的Abaqus脚本函数,传入stp文件路径和平移量参数。 4. **原有脚本的改造**:将原有脚本中处理单个stp文件的部分封装成一个函数,该函数接受两个参数:stp文件路径和平移量。 ### 步骤详解 #### 1. 安装必要的库(如果尚未安装) 在Abaqus自带的Python环境中,通常已经安装了`openpyxl`,但如果没有,需要手动安装。注意:Abaqus使用的是自带的Python,因此安装库时需使用其自带的pip(位于Abaqus安装目录下的`python`文件夹中)。 #### 2. 读取xlsx文件 假设xlsx文件的结构已知,例如,第一列是弹簧模型名称(stp文件名对应),第二列是平移量(假设有三个分量:dx, dy, dz,可能存储在三列中)。 我们可以使用`openpyxl`读取xlsx文件,并将数据存储为字典,键为弹簧模型名称(不包含扩展名),值为平移量列表。 #### 3. 遍历stp文件 使用`os.listdir`或`glob`模块获取指定文件夹下所有stp文件的路径。 #### 4. 主循环 对于每个stp文件,提取文件名(不含扩展名),然后在字典中查找对应的平移量。如果找到,则调用处理函数;否则跳过或记录错误。 #### 5. 封装原有脚本 将原有脚本中处理单个模型的部分改写为一个函数,例如`process_spring(stp_path, displacement)`,其中`displacement`是一个包含三个浮点数的列表。 ### 代码结构示例 ```python # 导入必要的模块 import os from openpyxl import load_workbook # 原有脚本中的函数和变量可能需要调整 # 假设我们有一个处理单个弹簧的函数 def process_spring(stp_file, displacement): # 这里放置原有脚本的主要步骤,包括: # 导入stp文件,应用平移量,运行分析等 # 注意:这个函数需要在Abaqus的Python环境中运行,因为它会调用Abaqus的API # 例如: # import part # my_model = mdb.Model(name='SpringModel') # my_part = my_model.PartFromGeometryFile(..., geometryFile=stp_file, ...) # ... 然后应用位移 ... pass # 主程序 def main(): # 配置路径 stp_folder = 'path/to/stp/files' # stp文件所在文件夹 xlsx_file = 'path/to/displacement_data.xlsx' # 平移量数据文件 # 读取xlsx文件 wb = load_workbook(xlsx_file) ws = wb.active # 获取活动工作表 # 假设第一行是标题,数据从第二行开始 # 假设第一列是模型名称,第二、三、四列分别是dx, dy, dz displacement_data = {} for row in ws.iter_rows(min_row=2, values_only=True): model_name = row[0] # 第一列 dx = row[1] dy = row[2] dz = row[3] displacement_data[model_name] = (dx, dy, dz) # 遍历stp文件夹 for stp_file in os.listdir(stp_folder): if stp_file.endswith('.stp') or stp_file.endswith('.step'): # 获取不带扩展名的文件名 base_name = os.path.splitext(stp_file)[0] # 查找对应的平移量 if base_name in displacement_data: displacement = displacement_data[base_name] stp_path = os.path.join(stp_folder, stp_file) # 调用处理函数 process_spring(stp_path, displacement) else: print(f"Warning: No displacement data found for {base_name}") if __name__ == '__main__': main() ``` ### 注意事项 1. **Abaqus Python环境**:此脚本必须在Abaqus的Python环境中运行(通过Abaqus命令行或CAE中的脚本运行),因为它依赖于Abaqus的模块(如`part`, `mdb`, `session`等)。 2. **路径问题**:确保提供的文件夹路径和文件路径正确,最好使用绝对路径。 3. **错误处理**:在实际应用中,应加入错误处理(如try-except)来应对可能的异常(如文件不存在、数据格式错误等)。 4. **性能考虑**:批量处理可能耗时,建议在循环中加入进度打印。 5. **原有脚本的兼容性**:原有脚本可能需要调整以适应函数封装,特别是全局变量和模型命名的处理(避免重复模型名,可以使用不同的模型名或每次处理后关闭模型数据库)。 ### 扩展 如果原有脚本已经比较复杂,可能涉及多个步骤(建模、划分网格、加载、求解、后处理等),需要确保每一步都正确重置或使用新的模型,避免模型之间的干扰。可以考虑在每次循环结束后关闭模型数据库或使用不同的模型名称。 ### 相关问题 1. 如何在Abaqus Python脚本中导入STP文件并创建部件? 2. 如何应用平移量到导入的部件上(移动部件的位置)? 3. 在批量处理中如何管理内存和避免模型名称冲突? 4. 如何将批量处理的结果(如位移、应力等)汇总输出到同一个报告文件中? [^1]: 参考引用内容。</think>### Abaqus Python脚本批量处理实现方案 要实现批量处理STP文件和平移量数据,需要将您的脚本改造为以下结构: ```python # -*- coding: utf-8 -*- from abaqus import * from abaqusConstants import * import os import xlrd # 用于读取Excel文件 import assembly def batch_process_springs(stp_folder, xlsx_path): """ 批量处理弹簧STP文件和平移量数据 :param stp_folder: STP文件存放目录 :param xlsx_path: 平移量数据Excel文件路径 """ # 1. 读取平移量数据 displacement_data = read_displacement_data(xlsx_path) # 2. 遍历STP文件夹 for stp_file in os.listdir(stp_folder): if stp_file.lower().endswith(('.stp', '.step')): stp_path = os.path.join(stp_folder, stp_file) # 3. 获取当前文件对应的平移量 spring_name = os.path.splitext(stp_file)[0] if spring_name in displacement_data: dx, dy, dz = displacement_data[spring_name] # 4. 执行处理流程 process_single_spring(stp_path, dx, dy, dz) else: print(f"警告: 未找到 {spring_name} 的平移量数据") def read_displacement_data(xlsx_path): """ 从Excel读取平移量数据 """ data_dict = {} workbook = xlrd.open_workbook(xlsx_path) sheet = workbook.sheet_by_index(0) for row_idx in range(1, sheet.nrows): # 跳过标题行 spring_name = sheet.cell_value(row_idx, 0) dx = sheet.cell_value(row_idx, 1) dy = sheet.cell_value(row_idx, 2) dz = sheet.cell_value(row_idx, 3) data_dict[spring_name] = (dx, dy, dz) return data_dict def process_single_spring(stp_path, dx, dy, dz): """ 处理单个弹簧模型 """ # 创建新模型 model_name = f"Spring_{os.path.basename(stp_path).split('.')[0]}" my_model = mdb.Model(name=model_name) # 导入STP文件 my_part = my_model.PartFromGeometryFile( name='SpringPart', geometryFile=stp_path, combine=False, dimensionality=THREE_D, type=DEFORMABLE_BODY ) # 应用平移量 assembly = my_model.rootAssembly instance = assembly.Instance(name='SpringInstance', part=my_part, dependent=ON) assembly.translate(instanceList=('SpringInstance',), vector=(dx, dy, dz)) # 后续处理步骤 (网格划分、分析步设置、提交作业等) # ... print(f"已处理: {stp_path} 平移量: ({dx}, {dy}, {dz})") # 执行批量处理 if __name__ == '__main__': stp_folder = "C:/Spring_STP_Files" # 修改为实际路径 xlsx_path = "C:/displacement_data.xlsx" # 修改为实际路径 batch_process_springs(stp_folder, xlsx_path) ``` ### 关键实现细节 1. **Excel数据读取**: - 使用`xlrd`库读取Excel文件 - 数据结构:第一列为弹簧名称,后续列为dx, dy, dz平移量 - 示例Excel格式: | 弹簧名称 | dx | dy | dz | |----------|-----|-----|-----| | spring1 | 0.5 | 0.0 | 1.2 | 2. **STP文件处理**: - 遍历指定文件夹中所有`.stp/.step`文件 - 通过`PartFromGeometryFile`导入几何 - 使用`assembly.translate()`应用平移量 3. **命名管理**: ```python model_name = f"Spring_{os.path.basename(stp_path).split('.')[0]}" ``` 确保每个模型有唯一名称,避免冲突 4. **错误处理**: - 检查STP文件Excel数据的匹配性 - 跳过缺少平移量数据的文件 ### 使用前的准备 1. **安装依赖库**: ```bash abaqus python -m pip install xlrd ``` 2. **文件结构示例**: ``` C:/Spring_STP_Files/ spring1.stp spring2.stp displacement_data.xlsx ``` 3. **执行脚本**: ```bash abaqus cae -noGUI script.py ``` ### 优化建议 1. **并行处理**: ```python from joblib import Parallel, delayed # 在batch_process_springs中使用 Parallel(n_jobs=4)(delayed(process_single_spring)(...) for ...) ``` 2. **日志记录**: ```python import logging logging.basicConfig(filename='batch_process.log', level=logging.INFO) ``` 3. **错误恢复**: ```python try: process_single_spring(...) except Exception as e: logging.error(f"处理失败 {stp_path}: {str(e)}") ``` ### 注意事项 1. 确保Excel文件未被其他程序打开 2. STP文件名Excel中的名称需精确匹配 3. 平移量单位需模型单位制一致 4. 大文件处理时增加内存设置: ```python session.journalOptions.setValues(replayGeometry=COORDINATE, memory=90) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值