• 七种程序设计模式


    CRM/ERP 企业管理软件中常见的七种程序设计模式

     

    管理软件中的常见代码设计模式,来自于业务上的需要,有不恰当的地方欢迎批评指正。

    1  RE-TRY 重试模式

    场景:在连接数据库服务器时,如果SQL Server数据库没有启动或正在启动,我们需要有一个连接重试的策略。发送邮件通知时,我们也需要在发送失败后,多次的尝试发送以保证邮件能到达目的用户。

    代码参考:

    int maxRetry = 30;
    
    int retryInterval = 10000;
    
    for (int i = 1; i <= maxRetry; i++)
    
    {
    
         try
    
         {
    
             //connect to the database server
    
          }
    
         catch (Exceptions exception)
    
         {
    
             if (i < maxRetry)
    
                  Thread.Sleep(retryInterval);
    
             else 
    
                 return; //返回或停止重试操作
    
         }
    
    }
    

    这种模式的主要是有一个重试行为,直到执行完成或是超过了约定的时间或次数则放弃。

    2 Before-Perform-After  检查-执行-传送模式

    场景:在做一项业务操作前,子类为了重写(override)基类的行为必须先做条件或环境检查,然后执行相应的业务操作,之后还可以将此次操作的结果继续传送到其它业务单元中。

    以打印报表为例子,业务窗体需要先检查当前登录用户是否具备打印权限,如无权限则取消本次操作。代码例子:

    protected internal virtual bool DoPerformPrint()
    
    {
    
    
    
    CancelableRecordPrintEventArgs e = new CancelableRecordPrintEventArgs(this.CurrentEntity, selectionForumlas, formulaFields, parameterFields);
    
    this.OnBeforePrint(e);
    
    if (this._beforePrint != null)
    
       this._beforePrint(this, e);
    
    if (e.Cancel)
    
       return false;
    
    
    
    
    
    this.Print(ref selectionForumlas, ref formulaFields, ref parameterFields);
    
    
    
    EventArgs args = new EventArgs();
    
    this.OnAfterPrint(args);
    
    if (this._afterPrint != null)
    
       this._afterPrint(this, args);
    
    }
    

    通过这段代码应该容易理解我说的这种设计模式,第一段是条件检查,如果我们在子类中传入参数e.Cancel=true,则

    此方法返回,不再执行Print方法。这种设计模式在事件机制中用的比较多。基类为了控制好业务单元整体的行为,同时又不失去灵活性,可以参照这种模式。

    3 TRY  尝试模式

    场景:尝试性的去执行某一个业务单元,并以它的结果来决定下一步的行为。

    比如我们加载数据,如果加载失败了,则下一步要阻止数据绑定,再比如传送文件,如果双方连接失败,则要阻止文件传送。

    代码例子:

    private void LoadData()
    
    {
    
      try
    
      {
    
    
    
         FindAndLoadData ()
    
         this._isDataLoaded = true;
    
      }
    
      catch (Exception exception)
    
      {
    
         this._isDataLoaded =false;
    
      }
    
    }
    

    这个模式的关键就在于定义的变量_isDataLoaded。在这个方法中,我们尝试去加载数据,如果有异常,则将此变量设为false表示加载失败,其它的业务单元检查到此变量的值以决定下一步操作。

    这种行为也有危害,它隐藏了真实的异常原因,常常用在一些界面操作中,频繁的抛出各种异常会让用户反感,这时可以参考这种模式,尝试操作失败后,根据状态值阻止错误进一步发生。

    4 BackgroundWorker 多线程工作

    场景:在一些业务计算或是读取数据等耗费时间的业务操作,比如物需求计算,工作单批次发套料,计划订单发放等业务中,耗费的时间相当多,这种模式可改善效率。

    应用代码例子:

    List<LoadMasterScheduleWorker> workers = new List<LoadMasterScheduleWorker>();
    
    for (int i = 0; i < MAX_RUNNING_THREAD; i++)
    
    {
    
       LoadMasterScheduleWorker worker = new LoadMasterScheduleWorker(this, mrp, mpsRows, loadedMpsItems);
    
       workers.Add(worker);
    
    }
    
    WorkerThreadBase.StartAndWaitAll(workers.ToArray());
    
    
    
    MAX_RUNNING_THREAD通常取当前系统的CPU个数(MAX_RUNNING_THREAD),在实际工作的线程中,采用下面的方法执行数据分配并启动相应的业务操作:
    while (_mpsRows.Count > 0)
    
    {
    
        DataRow mpsRow = null;
    
        if (!_mpsRows.TryTake(out mpsRow))
    
               break;
    
    
    
        LoadMasterSchedule(_mrp, mpsRow);
    
    }

    从Dictionary<DataRow>集合中不停的TryTake数据行(DataRow)来操作,直到取完所有的数据为止。

    需要注意前一段方法的最后一句StartAndWaitAll方法,这里要等待当前所有子线程执行完成,避免数据没有操作完就进行后面的业务操作。

    WorkerThreadBase的源代码参考这里。

    http://stackoverflow.com/questions/597590/c-sharp-threading-patterns-is-this-a-good-idea

    http://files.cnblogs.com/files/JamesLi2015/WorkerThreadBase.zip

    5 Data Class 数据类

    场景:减少代码中不必要的字符书写错误,改善程序可维护性。

    旧代码是这样的:

    DataTable query = new FastSerializableDataTable("BomRoutingTable");
    
    query.Columns.Add("BomNo", typeof(string));
    
    query.Columns.Add("SeqNo", typeof(decimal));
    
    query.Columns.Add("OpCode", typeof(string));

    应用数据类之后,代码是这样的:

    DataTable query = new FastSerializableDataTable("BomRoutingTable");
    
    query.Columns.Add(BomFields.BomNo, typeof(string));
    
    query.Columns.Add(BomFields.SeqNo, typeof(decimal));
    
    query.Columns.Add(BomFields.OpCode, typeof(string));
    
    
    
    
    
    class BomFields
    
    {
    
       public const string BomNo ="BomNo";
    
       public const string SeqNo  ="SeqNo";
    
       public const string OpCode ="OpCode";
    
    }

    新代码设计方式增加了一个类型定义,增加了一点复杂性,同时改善可维护性。创建DataTable的列来自于一个类型定义,如果有多个创建表的地方,则可显著改善书写效率。

    6 History 历史记录

    场景:ERP中标准物料清单需要通过ECN来变更,若是将此模式应用到其它业务,则做采购单修改或销售单修改等其它单据,也需要记录变更前的资料。

    例子代码:

    BomHistoryEntity bomHistory = new BomHistoryEntity();
    
    InitialBomHistoryHeaderInActivation(bomHistory, ecn, canUpdate);
    
    return bomHistory;

    变更记录相当于流水帐,记录每次业务修改前的值。变更记录还可以通过数据审计功能来实现,不过审计功能过于抽象,只能记录到表的字段变化,要实现两行数据记录字段值的精确比较,还需要开发接口程序以适应特定的业务单据。

    历史记录模式最大的杀手就是反审核,反过帐,若业务单据可以反复修改,审核之后退回,反审核又可以修改,这样反复的操作,会产生大量的历史数据。

    7 Hash Verify 哈希值验证

    场景:文件传送时,在文件传送完成后,需要验证文件传送前后的MD5值或SHA1是否一致以保证文件没有丢失字节。

    在一些重要的业务场景,比如转帐,充值,系统登录,在业务操作完成后我们还需要验证一下业务发生是否合理。

    代码例子:

    session = Login(userId, passwordHash, companyCode, languageCode, hostEntry.HostName, domainUserName, currentProcess.SessionId)
    
    string clientHash = Shared.GetMD5HashValue(string.Concat(new object[] { 
    
                        AssemblyVersion.Company, AssemblyVersion.Product, AssemblyVersion.Version, AssemblyVersion.FileVersion, AssemblyVersion.Product, session.UserId, session.UserGroup, session.CompanyCode, session.SessionId, session.LanguageCode, session.HostName, session.DomainUserName}));
    
                  
    
    string privateKey = "...";
    
    
    
    string serverHash = RSACryptionHelper.DecryptString(session.ClientHashValue, privateKey);
    
    if (string.CompareOrdinal(clientHash, serverHash) != 0)
    
       throw new AppException("Unable to login, unexpected error detected");
     

    第一行代码登录成功之后,服务器返回一个session会话对象,此对象包含一个ClientHashValue的MD5值,与此同时我们将客户端的登入信息,再次构造成一个字符串进行MD5处理,以比较两者的值是否一致,若不一致可能是非法登录请求,抛出异常,阻止登入系统。

    这个方法也可应用于多版本策略中,系统要阻止3.2版本的客户端登录到2.1版本的服务器端,注意到上面的代码中有Version参数,会抛出异常。这样可保证一台电脑安装多个版本的服务器端而不相互冲突。

  • 相关阅读:
    devexpress LayoutControl控件里面的内边距的消除
    NPOI 单元格高度和宽度的值设置解释
    NPOI 设置合并后的单元格的边框的解决方法
    ajax中参数traditional的作用
    kendo ui 遇到问题 Invalid or unexpected token的原因和解决方法
    kendo ui grid重置功能,重置所有数据
    mysql 8.*版本部署上以后用navcat能连上,但是系统连不上
    mysql 5.7的my.ini的位置在隐藏文件夹“ProgramData”下面
    获取kendo treeView上的选中项
    mysql按30分钟进行分组
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5538994.html
Copyright © 2020-2023  润新知