复杂业务简单化的一个小技巧

在复杂系统中,当对业务数据进行“删除”时(一般不允许对业务数据进行删除,只是举例) ,需要根据其它业务数据进行判断如:

  1.已生成出库单不允许删除,

  2.付款单已确认不允许删除,

  3.已经退换货则不允许删除。

  实际业务中可能会更加复杂。

当出现这种情况时将导致“删除”业务判断会非常多,而且会经常修改,很有可能当其它业务发生变化时需要重新去调整“删除”业务。

解决方法:为业务数据增加一个中间件“是否可以删除的标识”其它业务发生变化时修改此标识为false  其它业务修改时不能将此标识改为true 删除时只根据此标识做判断,可能会手动修改业务标识(高级操作)

不能将此标识改为true是因为 改为true需要去做很多验证,为了避免复杂性所以不能修改标识为true。

如:用户提出需求“自营店,可以修改价格,可以添加赠品,不扣预存款。非自营店,不能改价格,不能加赠品,扣款存款”

程序设计时最好别把 “自营或非自营”做为判断条件,而将“是否允许修改价格,是否允许添加赠品,是否扣除预存款”做为判断条件,对店铺(业务数据)增加相应的控制开关配置。

这样可能很好的提高程序扩展性,未来业务发生变化时可以不用修改代码。比如 需要"非自营店可能添加赠品"时。

对业务数据的控制开关配置可以使用key/value形式通一设计持久化保存,这些开关拒绝和业务数据的查询有联系。

注意事项:1.如果系统中大量使用开关配置,会经常会开关配置进行修改,需要考虑并发性,防止多个业务同时对相同的控制开发进行修改。

2.此类配置不会做为数据检索条件

以上是工作中记录内容,分享相应的处理代码,希望对你们有用

/// <summary>
    /// 数据配置类
    /// </summary>
    public class SmartConfig : BaseEntity
    {
        /// <summary>
        /// 类型
        /// </summary>
        [MaxLength(32)]
        public string Type { get; set; }
        /// <summary>
        /// 类型标识
        /// </summary>
        [MaxLength(32)]
        public string TypeIdentity { get; set; }
        /// <summary>
        /// 时间截
        /// </summary>
        [MaxLength(32)]
        public string Timestamp { get; set; }
        /// <summary>
        /// 配置
        /// </summary>
        public string Config { get; set; }
    }

这是Table对应的实体类,用于数据存储。

 /// <summary>
    /// 业务数据配置类
    /// 请勿直接new这个对象,使用SmartConfigService.Get 方法获取对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <remarks>2014-08-20 ywb 创建</remarks>
    public class Smart<T>
    {
        /// <summary>
        /// 请勿直接new这个对象,使用SmartConfigService.Get 方法获取对象
        /// </summary>
        /// <param name="typeIdentity"></param>
        /// <param name="timestamp"></param>
        /// <param name="config"></param>
        public Smart(string typeIdentity, string timestamp, T config)
        {
            this.Timestamp = timestamp;
            this.Type = typeof(T).Name.Replace("C_", "");
            this.TypeIdentify = typeIdentity;
            this.Config = config;
        }
        /// <summary>
        /// 业务数据类型
        /// </summary>
        public string Type { get; private set; }
        /// <summary>
        /// 业务数据标识
        /// </summary>
        public string TypeIdentify { get; private set; }
        /// <summary>
        /// 时间戳
        /// </summary>
        public string Timestamp { get; private set; }
        /// <summary>
        /// 配置
        /// </summary>
        public T Config { get; private set; }
    }

业务数据配置类

/// <summary>
    /// 菜 配置
    /// </summary>
    /// <remarks>
    /// 约定命名规则为 前缀“C_ ”+ “业务表名”
    /// 这种方式不太符合规范但是好用,配合VS智能提示写代码方便
    /// </remarks>
    public class C_Dish
    {
        public C_Dish()
        {
            //在构造函数中设置配置项的默认值
            NeedCooking = true;
            EnabledPrint = true;
            Return = false;
            AllowToCart = true;
        }
        /// <summary>
        /// 需要厨师烹饪
        /// </summary>
        [Description("需要厨师烹饪")]
        public bool NeedCooking { get; set; }
        /// <summary>
        /// 状态为“完成”时可退
        /// </summary>
        [Description("状态“完成”时能退")]
        public bool Return { get; set; }

        /// <summary>
        /// 启用打印
        /// </summary>
        [Description("启用打印")]
        public bool EnabledPrint { get; set; }

        /// <summary>
        /// 允许顾客点餐
        /// </summary>
        [Description("允许顾客点餐")]
        public bool AllowToCart { get; set; }

    }

这是具体业务拥有的配置,在构造函数中设置业务数据配置项的默认值

/// <summary>
    /// 配置Service
    /// </summary>
    public static class SmartConfigService
    {
        /// <summary>
        /// 更新 SySmartConfig
        /// 如果返回false 很可能是并发引起的,需要业务终止执行。
        /// </summary>
        /// <param name="type">业务类型</param>
        /// <param name="typeIdentity">业务类型标识</param>
        /// <param name="config">配置</param>
        /// <param name="timestamp">更新前的时间戳</param>
        /// <param name="newTimestamp">更新后的时间戳</param>
        /// <returns></returns>
        public static bool Update(string type, string typeIdentity, string config, string timestamp, string newTimestamp)
        {

            var flag = DB.Wdd.Database.ExecuteSqlCommand("update SmartConfigs set Config={0},Timestamp={1} where Type={2} and TypeIdentity={3} and Timestamp={4}"
                , config, newTimestamp, type, typeIdentity, timestamp) == 1;
            CacheMemory.Remove(string.Format(CacheKey.Domain_SmartConfig, type, typeIdentity));
            return flag;

        }
        /// <summary>
        /// 获取 SySmartConfig 如果数据库中没有,则创建并返回SySmartConfig
        /// </summary>
        /// <param name="type">业务类型</param>
        /// <param name="typeIdentity">业务类型标识</param>
        /// <param name="timestamp">时间戳</param>
        /// <returns></returns>
        public static SmartConfig Get(string type, string typeIdentity, string timestamp)
        {
            var cacheKey = string.Format(CacheKey.Domain_SmartConfig, type, typeIdentity);
            return CacheMemory.Get<SmartConfig>(cacheKey, () =>
            {
                var config = DB.Wdd.SmartConfigs.FirstOrDefault(p => p.Type == type && p.TypeIdentity == typeIdentity);
                if (config != null) return config;

                config = new SmartConfig()
                {
                    Config = string.Empty,
                    Timestamp = timestamp,
                    Type = type,
                    TypeIdentity = typeIdentity
                };
                DB.Wdd.AddEntity<SmartConfig>(config);
                return config;
            }, new TimeSpan(24, 0, 0));
        }

        /// <summary>
        /// 保存 业务数据配置
        /// 如果返回false 很可能是并发引起的,需要业务终止执行。
        /// </summary>
        /// <param name="smart">业务数据配置.</param>
        public static bool Save<T>(Smart<T> smart)
        {
            if (smart == null) throw new ArgumentNullException("smart");
            return Update(smart.Type, smart.TypeIdentify, Newtonsoft.Json.JsonConvert.SerializeObject(smart.Config), smart.Timestamp, Timestamp(DateTime.Now));
        }

        /// <summary>
        /// 获取 业务数据配置
        /// </summary>
        /// <param name="typeIdentity">业务类型标识.</param>
        public static Smart<T> Get<T>(string typeIdentity)
        {
            var type = typeof(T).Name.Replace("C_", "");
            var smartConfig = Get(type, typeIdentity, Timestamp(DateTime.Now));
            if (string.IsNullOrEmpty(smartConfig.Config)) smartConfig.Config = "{}";

            //转换出错异常不处理
            var config = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(smartConfig.Config);
            return new Smart<T>(typeIdentity, smartConfig.Timestamp, config);
        }

        /// <summary>
        /// 获取时间戳.
        /// </summary>
        /// <param name="dt">时间.</param>
        /// <returns></returns>
        private static string Timestamp(DateTime dt)
        {
            DateTime dt1 = new DateTime(1970, 1, 1);
            TimeSpan ts = dt - dt1;
            return ts.TotalMilliseconds.ToString();
        }
    }

这是接口层代码实现,开发人员只需要关心以下二个方法

public static bool Save<T>(Smart<T> smart)

public static Smart<T> Get<T>(string typeIdentity)

其它方法不需要关心,可改为private。

调用代码如下:

            var dish = SmartConfigService.Get<C_Dish>("1");
            dish.Config.NeedCooking = true;
            SmartConfigService.Save(dish);

保存的时候可以判断一下返回值如果为false,可能是因为并发,应结束业务处理

时间: 2024-10-03 23:53:18

复杂业务简单化的一个小技巧的相关文章

记录一个小技巧,在一个包下的多个main函数调试

在eclipse中,有好几个class想做测试,又不想工程太多,都写在了一个包里面,也方便import 但是每次运行的时候,eclipse都默认运行第一次建立的那个main函数. 要想运行其他的,需要做一下修改. 在工具栏中, 点击run旁边的下拉箭头,会出现一个下拉菜单. 点击Run Configurations 进入到配置界面 在标注红色的区域,上面那个是选取工程,下面那个是选取该工程的主函数 在这里点search,找到你刚写的新的主函数,如果里面还看不到,可以手动输入就好了 然后点击app

[每日一个小技巧] CentOS 下使用yum安装一类软件包

yum 提供了丰富的工具用于支持软件包的安装. 有时候需要安装不只只是一个的情况下,可以使用groupinstall选项. 首先可以通过grouplist查询对应的group信息,比如需要安装开发工具的情况下, 可以使用以下命令: $ yum groupinstall  "Development Tools" [每日一个小技巧] CentOS 下使用yum安装一类软件包,布布扣,bubuko.com

快速掌握iOS API的一个小技巧

快速掌握iOS API的一个小技巧 周银辉 iOS SDK和Developer Library中提供了各个类以及函数的帮助文档,这很棒,但要想了解整个库的大体结构(比如UIKit下有哪些类,他们的继承关系如何)这有些让人摸不着头脑,下面有个小技巧: 打开iOS Developer Library, 点击你想要了解的Framework,以UIKit为例,好多东西都是平级地放在一起的,根本看不下去了 然后在搜索框输入: Framework Reference,绝大多数情况下都会出现一个 xxx Fr

开大Stack的一个小技巧

在程序头部添加一行 #pragma comment(linker, "/STACK:16777216") 可有效开大堆栈 实验效果如下: 11330179 2014-08-05 18:28:17 Wrong Answer 4920 1687MS 7776K 1327 B C++ Jeremy_wu 11272238 2014-07-31 19:50:26 Wrong Answer 4891 62MS 2368K 1402 B G++ Jeremy_wu 下面是没添加这一行的运行结果 上

mac下cp命令的妙用(一个小技巧)

在项目开发中遇到了这样一种情况: 需要用一个干净的工程(export出来的,没有svn信息)去覆盖一个主干的工程(含有svn信息),然后提交代码:我们在mac系统中拷贝->粘贴到目标文件夹,只有2个选项:停止和全部替换:选择全部替换后,svn信息全部丢失了. 这时我们可以使用cp -r dir1 rootdir 来进行覆盖,dir1是那个干净工程的文件夹路径,和主干工程同名:rootdir是主干工程的上级目录,如此一来我们就实现了保留svn信息前提下覆盖文件夹的目的. mac下cp命令的妙用(一

汇道科技:一个小技巧让JAVA程序员顺利拿到理想Offer

有朋友跟我抱怨,明明平常对知识掌握的很精炼了,一到面试就会紧张,发挥失常,错失工作机会,这种情况就像"考前综合症",平常对知识点都掌握的很好,一到大场合就怯场,其实我们无论是在考试还是面试,都有很多技巧的.今天汇道科技小编分享一些面试技巧给各位程序员,让大家在面试时能更加得心应手,更顺利的拿到理想的offer. Java程序员面试的有哪些小技巧 01 笔试 笔试这个环节是很容易,一般由5至10个选择题+2至5个论述题+1至2个编程题组成. 我们保持平静心态--浏览所有题目--开答.答题

mysql不能启动问题分析的一个小技巧

场景:在迁移mysql5.5.25a数据目录(从/var/lib/mysql迁移至/data)后,无法启动数据库,执行service mysql start,提示pid无法更新,新旧数据目录下的错误日志均无任何信息.此时,没有太多mysql使用经验的同事估计已经傻眼了. 类似问题如何排查呢? 这里有个小技巧: 可以稍微改下/etc/init.d/mysql(先备份),里面的      $bindir/mysqld_safe --datadir="$datadir" --pid-file

Android开发中padding使用一个小技巧

在安卓应用开发中,有时要用到状态按钮(可点击时与不可点击时的背景不相同),而且产品要求的按钮大小是固定的.在不同的手机上按钮的文字显示可能有些异常(主要是在给按钮做背景时很容易出现),此时我们怎么处理呢?我们可以用到padding这个属性. 看看小例子: --------------------------就一个按钮,但是background是用xml文件写的状态selector-------------------------------- <Button android:id="@+i

wget一个小技巧

今天在装一个东西的时候,在网上看到了这样的命令 wget  http://xxxx.sh -o -  | sh  的用法 不太明白-o -的用法于是乎man wget,看到了下面的一段话 -O file       --output-document=file           The documents will not be written to the appropriate files, but all           will be concatenated together a