这篇文章还是对工作内容的总结,主要是总结一下这几天做的产品的客户化工作内容。

关于产品线工程中客户化的理论知识和概念,请见金根的《产品线工程》。具体的,OEA框架中的客户化理论,见:《软件产品线工程方法:如何在OpenExpressApp做客户化工作》。

本文主要从以下几个方面来叙述如何在OEA框架中设计和实现客户化框架:

  1. OEA客户化框架设计目标
  2. 方案设计
  3. 具体实现

设计目标

  1. 支持实体类的扩展。
  2. 支持实体扩展包的动态加载。
  3. 支持界面扩展及界面扩展包的动态加载。
  4. 各版本间自定义界面元素,可以基于现有的特定版本修改一些内容。
  5. 各版本间支持自定义内容文件,如果没有使用,则使用默认版本的内容文件。(内容文件是指:图片、帮助文档等。)

解释一下,基于OEA框架的GIX4项目是以领域实体为中心的架构。主版本中的领域实体,代表了产品功能“7、2、1”中的7和2 。7是所有版本都应该有的领域实体,2是可以进行配置以说明是否具备的领域实体,而1就是在主干之外,为特定版本开发的实体。所以以上目标中,支持对“2”的定制和对“1”的扩展是最重要的。

由于时间仓促,目前只能以上述内容为目标,以后可能还会添加一些内容。如,枚举值的客户化,DailyBuild客户化等。


方案设计

本次设计经过组内讨论,确定了具体的设计方向。这里主要对最重要的两项进行详细的叙述。

配置?

一般来说,要实现客户化,使用配置可能是最直接的想法。一开始我想也没想就觉得可能客户化的内容需要存储在配置文件中,可能是一个自定义的XML文档。但是,后来和朋友聊天过程中灵光一闪,真的要采用配置吗?这里根本不需要在运行时动态改变应用程序的行为,只要在编译期能够编译出不同的版本即可,所以我决定使用“应用程序定义”的方式来完成“配置”。而“定义”与配置不同点在于,定义是用代码写死的,程序运行期间不可更改。编译期根据定义编译不同的版本。

其实后来知道,产品线工程中的重点之一就是对产品的“可变性”进行管理。而可变性的实现机制有很多种,主要分三类:适配、替换、扩展,具体内容见:《软件产品线工程方法:如何在OpenExpressApp做客户化工作》。

设计之初,我认为客户化的应用程序配置应该满足:

  1. 可以有公共的配置,子配置如果设置了同样的项,则重写公共的配置。
  2. 简单可用的配置API

最后,我定出了以下的实现目标:

主干版本中有应用程序定义类ConfigMain,客户A和客户B分别有自定义的配置类ConfigA,ConfigB。

各客户的版本中,分别把他自己的配置类和主配置类结合,然后以配置文件的方式注入到整个应用程序中。

当应用程序读取某个配置项时,直接从注入的配置类中获取;此时,按照一定的寻找顺序,定位该配置项。如客户A的配置类为ConfigA + ConfigMain,则在寻找时,应该先在ConfigA中寻找,如果找不到,则在ConfigMain中寻找。

文件组织方式

各客户版本需要不同的文件来运行,这些文件主要是一些内容文件,如图片,xml,也包含少量的DLL。毫无疑问地,客户化工作需要对它们进行管理。

DLL文件的组织比较简单,只需要各客户版本把自己的DLL放在一个版本特定的目录下,程序动态加载就行了。

这里我定出了以下规则:所有需要客户化的DLL都放在客户各自的文件夹根目录下。

但是这里需要注意,这些代码文件需要在应用程序定义被加载之后,才会被应用程序加载。所以应用程序定义类需要被直接DI进来,这样,客户版本信息就可以在这些DLL加载之前被访问到,也就可以继续加载这些DLL了。

内容文件的组织不同于代码,这些文件很可能在运行时也需要被替换。所以这里的策略不能再使用“定义”的方式。需要有一定的文件寻址算法。以下是暂定方案:

所有需要客户化的文件都放在/Files/中。版本通用文件,则直接放在/Files/Common/中。各客户有自己的文件夹,如客户A有文件夹/Files/A/。文件夹名在配置类中标明。
程序中,可以文件寻找引擎指定要使用的文件的相对路径,如使用LOGO,则指定/Images/Logo.jpg。如果客户A在A中已经建立了/Files/A/Images/Logo.jpg文件,则返回此文件;否则返回的应该是/Files/Common/Images/Logo.jpg。

方案总结

使用定义而不使用配置的方式,防止了不必要的程序代码的开发。但是要注意定义的API的简便和易用性。

文件组织方式使得各客户文件完全分离,简化了Buid 版本的代码开发。这里主要注意路径寻址的实现。


具体设计

应用程序定义类的实现

为支持属性值的重写和融合,应用程序定义类直接使用OO的继承实现,通用的定义类作为基类,分支版本直接从它派生下来并重写新的属性。使用OO的方式可以很好地实现属性值扩展,例如,我们可以使用装饰模式来实现复杂的属性定义。

应用程序定义类中,应该组合一些分支对象,来进行更细粒度的定义。

下图是本次客户化中应用程序定义类的结构:

图1 应用程序定义类的结构

Freeable表示所有定义都是可以被冻结的。这些定义在一开始被设置好版本的值后,将会被冻结,所以内容不再改变,变为“不可变类”。一,这是其运行期不需要改变的体现;二,不可变类是高效的类。

PathDefinition是所有内容文件的路径定义,它使用了PathProvider类来为其提供内容文件路径寻址算法,同时,它使用内容文件的相对路径从PathProvider中获取真实路径。

UIInfo是视图信息的载体,该类是定义的重点,留待下一篇中介绍。

AppDefinition是整个应用程序定义类的基类,以DI实现单例模式,作为全局唯一的访问点。目前,它包含了一个UIInfo对象来提供视图信息和一个PathDefinition来提供文件路径。

以下主要给出AppDefinition类具体的代码:

/// <summary>
/// 应用程序的主干版本定义。
/// 同时,也是分支版本定义的基类。
/// </summary>
public abstract class AppDefinition : Definition
{#region SingleTonprivate static AppDefinition _instance;/// <summary>/// 提供一个静态字段,作为全局唯一的访问地址。/// 注意:本类并没有直接设计为单例模式!/// </summary>public static AppDefinition Instance{get{if (_instance == null) throw new InvalidOperationException("请先设置该属性。");return _instance;}set{if (value == null) throw new ArgumentNullException("value");if (_instance != null) throw new InvalidOperationException("该属性只能被设置一次。");_instance = value;}}#endregion/// <summary>/// 查找文件路径的查找算法提供器。/// </summary>private PathProvider _pathProvider;/// <summary>/// 在使用所有属性前,需要主动调用此方法来进行初始化。/// </summary>protected override void InitCore(){base.InitCore();this.CheckUnFrozen();this._pathProvider = new PathProvider();if (!string.IsNullOrWhiteSpace(this.BranchAppName)){this._pathProvider.AddBranch(this.BranchAppName);}//初始化所有文件路径this.Pathes = this.CreatePathes();this.Pathes.SetPathProvider(this._pathProvider);this.Pathes.Initialize();//创建元数据库this.UIInfo = this.DefineUI();this.UIInfo.Initialize();}protected override void OnFrozen(){base.OnFrozen();FreezeChildren(this.UIInfo, this.Pathes);}/// <summary>/// 分支版本名。/// 同时,这个也是客户化文件夹的名字。/// 分支版本定义,需要重写这个属性。/// </summary>protected virtual string BranchAppName{get{return null;}}#region 文件/// <summary>/// 创建所有路径的定义。/// 子类重写此方法,用于添加更多的路径信息定义。/// </summary>/// <returns></returns>protected virtual PathDefinition CreatePathes(){return new PathDefinition();}/// <summary>/// 应用程序中所有使用到的需要客户化的路径集。/// </summary>public PathDefinition Pathes { get; private set; }#endregion#region DLL/// <summary>/// 获取所有此版本中需要加载的实体类Dll集合。/// </summary>/// <returns></returns>public string[] GetEntityDlls(){return this._pathProvider.MapAllPathes("Library", true);}/// <summary>/// 获取所有此版本中需要加载的模块Dll集合。/// </summary>/// <returns></returns>public string[] GetModuleDlls(){return this._pathProvider.MapAllPathes("Module", false);}#endregion#region UIInfo/// <summary>/// 应用程序中所有可用的视图信息。/// </summary>public UIInfo UIInfo { get; private set; }/// <summary>/// 子类重写此方法,用于初始化产品视图定义。/// 重点实现!/// </summary>/// <returns></returns>protected virtual UIInfo DefineUI(){return new UIInfo();}#endregion
}

子版本的定义需要重写父类的DefineUI方法进行自己的版本信息定义,如,通用版本的实现:

namespace Common.Definition
{/// <summary>/// 通用版本的产品定义/// </summary>public class AppDefinition : OpenExpressApp.MetaModel.Customizing.AppDefinition{/// <summary>/// 子类重写此属性以指定是否包含合同。/// </summary>protected virtual bool IncludeContract{get{return true;}}/// <summary>/// 这里加入东方版本需要的特定的视图信息/// </summary>/// <returns></returns>protected override UIInfo DefineUI(){var ui = base.DefineUI();ui.Entity<CBFGQBQItemTitle>().EntityProperty(t => t.Code).ShowInLookup().ShowInList().Set_ListMinWidth(200);ui.Entity<CBSummary>().EntityProperty(t => t.PBSId).Show().Set_ListMinWidth(500);ui.Entity<CBNormSummary>().EntityProperty(t => t.PBSId).Show().Set_ListMinWidth(100);//在基类上定义视图信息,这个类的所有子类如果没有显式设置其它的值,则会使用基类的属性。ui.Entity(typeof(BQNormItemTitleBase)).EntityProperty("AllCode").Show().Set_ListMinWidth(200);this.DefineContractDll(ui);return ui;}/// <summary>/// 定义合同模块的显示。/// </summary>/// <param name="ui"></param>private void DefineContractDll(UIInfo ui){if (this.IncludeContract){ui.Entity<ContractBudget>().UnVisible();ui.Entity<RealContractBudget>().Visible();ui.UnVisible(CCN.ShowPCIBQitemCommand, CCN.EntityShowBQCommand,CCN.MeasureShowBQCommand, CCN.ResourceShowBQCommand,CCN.ProjectIndicatorsCalculationCommand);}else{ui.UnVisible(typeof(RealContractBudget), typeof(ContractSubjectType),typeof(ProjectContractSubject), typeof(ContractIndicatorQueryObject));ui.UnVisible(CCCN.ShowPCIBQitemCommand, CCCN.EntityShowBQCommand,CCCN.MeasureShowBQCommand, CCCN.ResourceShowBQCommand);}}}
}

程序在启动时,从配置中注入AppDefinition,然后调用其初始化操作和冻结方法即可:

public partial class App : Application
{public App(){this.InitAppDefinition();}/// <summary>/// 定义软件运行时版本/// </summary>private void InitAppDefinition(){var appDefType = Type.GetType(AppConfig.Instance.AppDefinitionClass, true, true);AppDefinition.Instance = Activator.CreateInstance(appDefType) as AppDefinition;AppDefinition.Instance.Initialize();AppDefinition.Instance.Freeze();}

配置文件:

<add key="AppDefinitionClass" value="Common.Definition.AppDefinition, Common.Definition"/>

限于篇幅,今天就先总结到此。下一篇主要是把客户化框架的设计讲完,然后再下一篇可能是GIX4项目中分离原有DLL的应用。

转载于:https://www.cnblogs.com/zgynhqf/archive/2010/09/20/1832019.html

基于OEA框架的客户化设计(一) 总体设计相关推荐

  1. (附源码)nodejs+mysql+node基于vue框架的游戏商城设计及开发 -《夜幕》毕业设计262127

    Node.js<夜幕>游戏商城的开发 摘 要 现今人们的生活方式逐渐丰富,电脑和网络已经融入了人们生活中的滴滴点点,无时不刻的影响着我们的日常生活,网络游戏已经进入到了大多数人的生活之中. ...

  2. nodejs+mysql+node基于vue框架的游戏商城设计及开发 毕业设计-附源码262127

    Node.js<夜幕>游戏商城的开发 摘  要 现今人们的生活方式逐渐丰富,电脑和网络已经融入了人们生活中的滴滴点点,无时不刻的影响着我们的日常生活,网络游戏已经进入到了大多数人的生活之中 ...

  3. 记录这两年的学习以及简述这次基于springboot框架的课程设计和打war包部署的坑

    一.前言 一想都是满满的经历呀.如果不想看我废话,可以直接跳到后面. 1 关于编程 1.1 C.C++ 作为一名软件工程生,编程是我以后自力更生的必备技能.奈何大一的我不懂得轻重,基本没有把精力放在编 ...

  4. 基于SpringBoot框架的药品在线销售系统的设计与实现

    药品在线销售系统是为药品商家提供的在线销售管理系统,本系统的研发设计能够增加药品商家的药品宣传和推广,提升客流量和订单量,增加商家的营业收益.原有的药品销售系统管理采用手工管理的方式,各种药品宣传和订 ...

  5. OEA 框架中集成的 RDLC 报表介绍

    之前 OEA 一直用着一个 Delphi 开发的报表,所以两年来我一直就想在 OEA 中构建一个纯 .NET 的报表模块,但是一想到要开发复杂的报表引擎和设计器就觉得麻烦.所以这事一直拖着.最近开始研 ...

  6. 基于SSM框架的校园论坛设计与实现(MySQL、JSP)

    此文章记录了本人毕业设计的设计与实现过程,欢迎大家交流讨论. 目录 摘 要 第1章 绪 论 1.1 课题背景及研究的目的和意义 1.2 国内外研究现状 1.3 本文的主要研究内容 第2章 系统总体设计 ...

  7. 基于SpringBoot框架的古风乐曲网站的设计与实现 毕业设计-附源码271611

    springboot古风乐曲网站 摘 要 随着社会的发展,社会的方方面面都在利用信息化时代的优势.互联网的优势和普及使得各种系统的开发成为必需. 本文以实际运用为开发背景,运用软件工程原理和开发方法, ...

  8. 基于SpringBoot框架的古风乐曲网站的设计与实现毕业设计源码271611

    springboot古风乐曲网站 摘 要 随着社会的发展,社会的方方面面都在利用信息化时代的优势.互联网的优势和普及使得各种系统的开发成为必需. 本文以实际运用为开发背景,运用软件工程原理和开发方法, ...

  9. 基于SSM框架的音频分享平台的设计与实现计算机毕业设计源码74192

    摘 要 在信息时代飞速发展的今天,人们获取信息的方式基本来源于互联网.互联网在人们娱乐生活上发挥着重要作用.传统的音乐网站大同小异,已经无法满足所有人的需要.基于SSM框架的音频分享平台可以让不同年龄 ...

最新文章

  1. 监督学习、非监督学习、强化学习都是什么?终于有人讲明白了
  2. python画代码-python画樱花树代码 具体代码介绍
  3. opencv 训练人脸对比_【项目案例python与人脸识别】基于OpenCV开源计算机视觉库的人脸识别之python实现...
  4. gsm,gprs,cmwap,cmnet,3g,TD-SCDMA,CDMA2000,WCDMA
  5. html如何显示带有记号的文本,如何使用Wicket设置HTML锚标记的显示文本?
  6. (STL,vector)木块问题
  7. 来了来了,HTML6展望!
  8. 那些基础的线程知识,你都懂了吗?| CSDN 博文精选
  9. hdoj1160:FatMouse's Speed(dp+最长递减子序列思想+数组巧妙记录输出)
  10. 构造者模式(Builder)
  11. 2020年全国大学生数学建模竞赛优秀论文
  12. 手机如何把PDF文件压缩的小一点?教你手机压缩文件方法
  13. 拆弹专家【爆改车间主任】学习笔记(2)小结
  14. 移动直播技术秒开优化经验(含PPT)
  15. 走方格跳格子(dp,递归,排列组合三种方法)
  16. 枚举 _枚举的其他应用
  17. 一个常见的文字无缝滚动效果
  18. 从燃油车布局新能源,汽车服务商们谋破局
  19. BFS广度优先搜索算法//宽度优先搜索算法
  20. 研发内控平台设计简介

热门文章

  1. 4.3 深层网络中的前向传播
  2. python刷阅读_简单的37行python爬虫刷CSDN博客阅读数
  3. 调查VMware View Composer失败代码(2085204)
  4. Socket 核心原理分享
  5. 信贷系统学习总结(2)——现金贷之借贷模式与前端产品
  6. Docker学习总结(22)——Docke run命令详解
  7. android主题资源,使用 Theme Editor 设计应用主题背景
  8. dockerfile 创建自定义的tomcat服务
  9. 构架https服务器
  10. uva 11374(Dijkstra) HappyNewYear!!!