文档章节

SpecificationPattern-规格模式

夏至如沫
 夏至如沫
发布于 2013/10/25 17:49
字数 1909
阅读 413
收藏 7

妈妈说我从小记性就不好,一切东西都是要记下来才行的。---原文是CodeProject 上的 Specification pattern in C#,觉得很不错,自己拿出来消化下跟大家分享。 首先什么是规格模式,百度了一下,竟然百科里没有,那就把原著里搬出来用吧。规格模式,根据维基百科上的解释,是一种特殊的软件设计模式,可以使用布尔逻辑将业务规则组合在一起构成新的业务规则。简单地说,每一粒的业务规则都是独立的并且基于单一职责的原则(SRP),我们可以使用加、减、非等简单逻辑将业务规则连接成为新的复合规则。这些业务规则就是我们这里说的规格。 曾经做个一个很脆的项目,是布艺加工行业的,比如现在有一个窗帘上用的布花,会有各种高端、大气、上档次的种类,当然每一种类都是要满足这个档次的各种规格要求。比如,普通屌丝家的布花也就规定下大小,形状,颜色而已,高端人士就有新的规格标准,土豪家的布花一定要大,一定要镀金,一定要有小的布花装饰在上面,等等云云。其实当初写代码的时候就是各种 && 和 && 加在一起验证是否合格的。现在想起来规格模式应该会适用。然后就可以开始尝试构建个模式了。当然为了省力,原文是用手机做示例,我肯定只负责粘代码了。

1.规格接口(粒度)

<!-- lang: c# -->
/// <summary>
/// 规格接口【表示最小粒度的单位】
/// </summary>
/// <typeparam name="T"></typeparam>
public interface ISpecification<T>
{
    /// <summary>
    /// 规格的自描述(是否符合规格定义)
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    bool IsSatisfiedBy(T o);
    /// <summary>
    /// 规格可以被用来复合形成行的复杂规格
    /// </summary>
    /// <param name="specification"></param>
    /// <returns></returns>
    ISpecification<T> And(ISpecification<T> specification);
    ISpecification<T> Or(ISpecification<T> specification);
    ISpecification<T> Not(ISpecification<T> specification);
}

这个很好理解,本着接口是万能的原则,就把规格的各种基本操作定义在接口中。

2.抽象的规格基类

<!-- lang: c# -->
/// <summary>
/// 可以被复合的规则的抽象类
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class CompositeSpecification<T> :ISpecification<T>
{
    /// <summary>
    /// 是否符合规则【指定为子类必须实现的抽象方法】
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public abstract bool IsSatisfiedBy(T o);


    public ISpecification<T> And(ISpecification<T> specification)
    {
        return new AndSpecification<T>(this, specification);
    }

    public ISpecification<T> Or(ISpecification<T> specification)
    {
        return new OrSpecification<T>(this, specification);
    }

    public ISpecification<T> Not(ISpecification<T> specification)
    {
        return new NotSpecification<T>(specification);
    }
}

无论如何这些规格还是业务规则也好,最后都应该是可以一眼就看明白,并且我们可以直接重复使用的东西,所以应该把规格变为可以实例化的类,然后就应当实现接口中的全部方法了。本来读到这里的时候以为 AndSpecificationOrSpecificationNotSpecification 只是方法的重载,以便于阅读和封装调用。然后就发现在抽象基类里根本找不到,才注意到 new 关键字,原来竟然是新的类型。

<!-- lang: c# -->
 /// <summary>
/// 规格的逻辑加法
/// </summary>
/// <typeparam name="T"></typeparam>
public class AndSpecification<T> : CompositeSpecification<T>
{
    /// <summary>
    /// 加运算左参数
    /// </summary>
    ISpecification<T> leftSpecification;
    /// <summary>
    /// 加运算右参数
    /// </summary>
    ISpecification<T> rightSpecification;
    /// <summary>
    /// 加运算
    /// </summary>
    /// <param name="left"></param>
    /// <param name="right"></param>
    public AndSpecification(ISpecification<T> left, ISpecification<T> right)
    {
        this.leftSpecification = left;
        this.rightSpecification = right;
    }
    /// <summary>
    /// 重新实现自描述
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public override bool IsSatisfiedBy(T o)
    {
        return this.leftSpecification.IsSatisfiedBy(o)
            && this.rightSpecification.IsSatisfiedBy(o);
    }
}

楞了一下想想,本来组合加在一起的规格就应该是新的类型。那么现在规格有了,实现规格的方法也有了。那么就想像一下我们应该怎么用吧。假如现在我们有了这么多手机:

<!-- lang: c# -->
 List<Mobile> mobiles = new List<Mobile>()
        {
            new Mobile(BrandName.Apple,Type.Smart,5888),
            new Mobile(BrandName.MEIZU,Type.Smart,2400),
            new Mobile(BrandName.Nokia,Type.Basic,700),
            new Mobile(BrandName.Samsung,Type.Smart,3200),
            new Mobile(BrandName.MI,Type.Smart,1600),
            new Mobile(BrandName.Htc,Type.Smart,1200),
            new Mobile(BrandName.CoolPad,Type.Smart,999)
        };

然后定义一个新的规格标准(只要三星的手机):

<!-- lang: c# -->
ISpecification<Mobile> samsungExpSpc

但是 ISpecification 只是一个接口,怎么实例化呢,难道是实现一个继承于CompositeSpecification 的类吗?当然这是我自己太笨,或者不认真没有认真思考一开始就明确定义的反省结构,其实只需要使用工厂方法创建出这个实例就行。先看一下完整的创建代码吧:

<!-- lang: c# -->
ISpecification<Mobile> samsungExpSpc = 
            new ExpressionSpecification<Mobile>(p => p.BrandName == BrandName.Samsung);

第一次看的时候觉得很神奇,怎么就用Lamda 表达式就创建了新的规格,不是应该创建相应的子类吗?说实话直到开始码这篇日记的时候才明白,刚才上面 CompositeSpecification 的代码并不是规格的定义,而是定义规格可以进行什么样的运算的。规格定义其实已经被延迟到使用的时候了,这也才是使用泛型的真正用意吧。已经看出来了,ExpressionSpecification 就是工厂方法,

/// <summary>
/// 工厂类(可以使用Lamba表达式创建规格)
/// </summary>
/// <typeparam name="T"></typeparam>
public class ExpressionSpecification<T> : CompositeSpecification<T>
{
    private Func<T, bool> expression;
    public ExpressionSpecification(Func<T, bool> expression)
    {
        if (expression == null)
            throw new ArgumentNullException();
        else
            this.expression = expression;
    }

    public override bool IsSatisfiedBy(T o)
    {
       // 使用传入的函数回调验证是否符合规格要求(这是让我觉得最神奇的地方)
        return this.expression(o);
    }
}

到这里应该可以看清楚结构状况了,就是通过 Lamda 表达式的形式创建我们规格。

<!-- lang: c# -->
private Func<T, bool> expression;

在创建之后就可以自由的使用了,各种复杂的规格定义也可以组合使用了。

<!-- lang: c# -->
       // 三星品牌
        ISpecification<Mobile> samsungExpSpc = 
            new ExpressionSpecification<Mobile>(p => p.BrandName == BrandName.Samsung);
        // 魅族品牌
        ISpecification<Mobile> meizuExpSpc =
            new ExpressionSpecification<Mobile>(p => p.BrandName == BrandName.MEIZU);
        // 三星或者魅族
        ISpecification<Mobile> samsungAndMeiZu = samsungExpSpc.And(meizuExpSpc);

好吧,终于消化完了,最后原著大神还很细心地提醒我们如果不使用 Lamda 表达式创建地话就无法适应在运算规则中的泛型结构,所以工厂返回结果时应当指明类型:

<!-- lang: c# -->
/// <summary>
/// 工厂方法(不使用Linq 表达式创建规格)
/// </summary>
/// <typeparam name="T"></typeparam>
public class PremiumSpecification<T> : CompositeSpecification<T>
{
    private int cost;
    /// <summary>
    /// 价格大于 传入参数 的规格
    /// </summary>
    /// <param name="cost"></param>
    public PremiumSpecification(int cost)
    {
        this.cost = cost;
    }
    /// <summary>
    /// 覆盖父类定义实现自描述
    /// </summary>
    /// <param name="o"></param>
    /// <returns></returns>
    public override bool IsSatisfiedBy(T o)
    {
        // 为了适应特定的结构,在一些处理时时应当指明转化类型
        // 或者增加泛型的约束条件         where T : Mobile
        return (o as Mobile).Cost >= this.cost;
    }

最后附上博客园里设计模式的系列文章,向大神们致敬..

.NET设计模式系列文章

====================================================================

PS 最近正在做前期的设计测试,正好想到 规格模式 挺符合场景的,可能就可以正式搬进项目代码中了 再次 Mark 下 在此输入图片描述

虽然已经被你妮DOUBLE KILL(拒绝我俩次了)了,希望你安好

在此输入图片描述

===================================================================== 示例代码加入了窗体无边框和支持,有兴趣的可以 Down 下

示例代码:规格模式 - MyGril

© 著作权归作者所有

共有 人打赏支持
夏至如沫

夏至如沫

粉丝 11
博文 38
码字总数 23667
作品 0
郑州
后端工程师
私信 提问
ESS控制台发布新功能:创建多实例规格的伸缩配置

背景 原弹性伸缩ESS服务限定,生效的伸缩配置中只能对应一种实例规格,这样就会存在如果生效的配置中的实例规格的库存不足(高配实例规格通常更容易出现库存不足的情况)时, 用户配置好的伸...

charles晟
03/28
0
0
无线plc,无线开关量,无线io模块,wifi开关量,io控制卡,无线数据采集RTU模块

标题: 基于lora及wifi无线通信的无线PLC在物联网远程IO控制场合中的应用 标签: 无线plc,无线开关量,无线io模块,wifi开关量,io控制卡 文档介绍: 本文档描述lora和wifi无线通信方式在远程开...

zongkezhikong
07/05
0
0
无线plc,无线开关量,无线io模块,wifi开关量,io控制卡,无线数据采集RTU模块

标题: 基于lora及wifi无线通信的无线PLC在物联网远程IO控制场合中的应用 标签: 无线plc,无线开关量,无线io模块,wifi开关量,io控制卡 文档介绍: 本文档描述lora和wifi无线通信方式在远程开...

zongkezhikong
07/05
0
0
[功能优化] 包年包月实例和按量实例均已支持入门级变配企业级规格

目前ECS控制台在升降配-升级配置中,支持经典网络实例进行跨系列的迁移升级,从入门级实例升级到企业级实例规格。 现在,和也已支持从入门级变配到企业级规格。 什么是企业级实例?什么是入门...

郁苍
06/25
0
0
模拟量无线采集传输,模拟量输出,模拟量无线传输,0~5v无线采集传输,0~20ma

ZKA-XX-WIFI 使 用 说 明 书 【 】绝密 【 】NDA 【X】公开 版权声明 1. 深圳市综科智控科技开发有限公司保留对该产品进行改进、完善的权利,所以我们不能保证本手册与您所购买的产品完全一致...

zongkezhikong
07/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Kafka+Flink 实现准实时异常检测系统

1.背景介绍 异常检测可以定义为“基于行动者(人或机器)的行为是否正常作出决策”,这项技术可以应用于非常多的行业中,比如金融场景中做交易检测、贷款检测;工业场景中做生产线预警;安防...

架构师springboot
11分钟前
1
0
DecimalFormat 类基本使用

/* * DecimalFormat 类主要靠 # 和 0 两种占位符号来指定数字长度 * 0 表示如果位数不足则以 0 填充 * # 表示只要有可能就把数字拉上这个位置 * */ public static void main(String[] args){...

嘴角轻扬30
28分钟前
3
0
This APT has Super Cow Powers.

在Debian/Ubuntu上,apt包管理器内嵌着一个彩蛋. 如果你在命令行界面输入 apt help 在最后一行能找到This APT has Super Cow Powers. 说明该apt具有超级牛力 牛力是个什么梗? 则说明你的系统...

taadis
46分钟前
2
0
起薪2万的爬虫工程师,Python需要学到什么程度才可以就业?

爬虫工程师的的薪资为20K起,当然,因为大数据,薪资也将一路上扬。那么,Python需要学到什么程度呢?今天我们来看看3位前辈的回答。 1、前段时间快要毕业,而我又不想找自己的老本行Java开发...

糖宝lsh
55分钟前
6
0
携手开发者共建云生态 首届腾讯云+社区开发者大会在京举办

本文由云+社区发表 北京时间12月15日,由腾讯云主办,极客邦科技、微信、腾讯TEG协办的首届腾讯云+社区开发者大会在北京朝阳悠唐皇冠假日酒店举办。在会上,腾讯云发布了重磅产品开发者平台以...

腾讯云加社区
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部