狠狠撸

狠狠撸Share a Scribd company logo
使用顿厂尝改善软件设计
    金明
对于我
??   金明
??   ThoughtWorks高级咨询师
??   InfoQ中文站主编
??   爱好敏捷、编程、读书、体育运动
??   联系方式
     –? 金明i@weibo
     –? mingjin@twitter
聊聊软件设计那些事儿...
为什么我们的闯补惫补程序里面那么多齿惭尝文件?
为什么我们的闯补惫补程序里面那么多齿惭尝文件?

为什么我们需要面向切面编程(AOP)?
为什么我们的闯补惫补程序里面那么多齿惭尝文件?

为什么我们需要面向切面编程(AOP)?

为什么大家对Ruby on Rails仍然津津乐道?
为什么我们的闯补惫补程序里面那么多齿惭尝文件?

为什么我们需要面向切面编程(AOP)?

为什么大家对Ruby on Rails仍然津津乐道?

面向对象编程的下一个“革命”是什么?
长期以来,
我们一直使用通用目的型语言
进行软件设计
但是,
我们还缺少什么?
上下文
The context
Subject Matter Experts,
难于与领域专家、需求人员沟通
  Business analysts...
表达性
使用顿蝉濒改善软件设计
通用目的型
 编程语言
 还不够!


   C##Erlang#
      #
 C++#Java# #
      Python
Ruby# Groovy#
  Fortran#
         C#
不够?
 缺什么?
不够?
 缺什么?
少即是多
更抽象、更内聚的DSL或许是解决方案
顿厂尝早已充斥我们周围
graphviz

                                                  LINQ
ant                  rake


                             Hibernate Query Language
       regular expressions


              顿厂尝早已充斥我们周围
SQL                                                             make


                                JMock expectations
                                                          FIT
      struts-config.xml


                                      rails validations
            CSS
SQL
^[w-.]+@([w-]){2,4}$
Visual!
DSL
?? Domain Specific Language
?? 领域专用语言

?? “两天以后”的DSL例子
  –? 2.days.from.today
定义

      a"computer"programming"language"of""
       limited"expressiveness"focused"on"a""
                par4cular"domain"
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""5"Mar4n"Fowler"
定义

      a"computer"programming"language"of""
       limited"expressiveness"focused"on"a""
                par4cular"domain"
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""5"Mar4n"Fowler"
定义

      a"computer"programming"language"of""
       limited"expressiveness"focused"on"a""
                par4cular"domain"
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""5"Mar4n"Fowler"
定义

      a"computer"programming"language"of""
       limited"expressiveness"focused"on"a""
                par4cular"domain"
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""5"Mar4n"Fowler"
让我们点杯星巴克拿铁
例子:咖啡订单DSL
?? 星巴克咖啡订单
“一杯超大杯(Venti)、低咖(half-caf)、
   脱脂(non-fat)、无奶沫、不加鲜奶油(no
   whip)的拿铁(latte)”
?? 传统的实现
上面例子中的问题
?? 依赖于技术实现API
?? 代码与我们向服务员下单的描述不匹配
?? 阅读性不佳,难以验证
使用顿蝉濒改善软件设计
DSL style




?? 更好地展现了代码意图
?? 利用了Ruby特性
 –? 无类型声明
 –? 无括号参数列表
 –? 元编程支持
基于Java的咖啡订单DSL
API 方式
CoffeeOrder order = new Latte(VENTI,
  HALF_CAF, NONFAT_MILK);
CoffeeOrder coffee = order.prepare(false);


DSL 方式
CoffeeOrder order = new
  Latte().size(VENTI).caffeine(HALF).milk(NO
  NFAT).foam(FALSE);
CoffeeOrder coffee = order.prepare();
其他的展现形式?
?? XML
<Order type=“Latte”>
  <caffeine>half</caffeine>
  <milk>nonfat</milk>
  <foam>false</form>
  <whip>false</whip>
</Order>
?? 英语
“Venti half-caf, non-fat, no foam, no whip
   latte”
“Venti latte with no whip cream, no foam,
   non-fat milk, half-caffeine”
DSL的两种类型
外部DSL                    内部DSL
?? 与宿主语言无关               ?? 以宿主语言进行编写
?? 执行需要编译器或者             ?? 基于约定地使用宿主
   解释器                      语言语法的子集
?? 可以是图形化的               ?? 有时也被称为嵌入式
                            DSL或者流畅接口




               ThoughtWorks 2008
外部DSL
?? 需要构造解析器以处理特定的语法
?? sql, make files, xml config files,
   regular expressions
优势
?? 语法不受限制
?? 运行时执行
不足
?? 一开始可能简单,但可能变得丑陋和复杂
?? 构造解析器比较困难
?? 缺乏工具的支持
内部DSL
?? 在宿主语言之上进行扩展
?? Rake, gradle, Jmock Expectations,
   Xspecs
优势
?? 不必编写、调试一门新语言
?? 宿主语言自身强大的能力
?? 滨顿贰等工具的支持更好
不足
?? 受宿主语言的特性限制
语言工作台
?? Language Workbench
?? 提供了图形界面的、元建模以及/或者代码
   生成,以定义和使用DSL
 –? Microsoft DSL tool for Visual Studio
 –? Intentional Software
 –? Eclipse EMF
 –? JetBrains Meta Programing System
DSL
?? 使用更具表达性语言而非通用目的型语言
?? 在开发人员与业务专家之间共享共同的理
   解
?? 业务专家可以帮助设计应用程序的业务逻
   辑
?? 避免技术代码噪音掩盖了业务逻辑代码
?? 清晰地将业务逻辑与应用程序代码分离
?? 业务规则主导开发
DSL的优势
?? 增加开发生产率
 –? 清晰的代码更易于演进
 –? 易于达成,但影响相对较小
?? 易于与领域专家交流
 –? 定义系统行为的通用语言
 –? 易于阅读胜于易于编写
 –? 难以达成,但影响很大
创建DSL
?? 由底向上(Framework Out)
 –? 基于框架或者库定义DSL
?? 由顶向下(Language In)
 –? 与使用者一起设计DSL
 –? 再驱动DSL实现与其语义模型
?? 遗留系统打补丁(Patch Legacy)
 –? 为遗留系统自动生成其“拙劣”的代码
内部顿厂尝的几种形式
computer()
  .processor()
    .cores(2)
    .i386()
  .disk()
    .size(150)
  .disk()
    .size(75)
    .speed(7200)
    .sata()
  .end();
模型与构造器
       Expression Builder                    Semantic Model
       (fluent)                              (push-button)


             Computer"Builder"                       Computer"
computer()
  .processor()                   new Computer(processor, disks)
    …                            new Processor(…)
  .disk()


                 Disk"Builder"                          Disk"

  .size(75)
  .speed(7200)                   new Disk(75, 7200, Disk.Interface.SATA)
  .sata()
相同的问题,不同的DSL
computer()         computer();
  .processor()       processor();
    .cores(2)          cores(2);
    .i386()            processorType(i386);
  .disk()            disk();
    .size(150)         diskSize(150);
  .disk()            disk();
    .size(75)          diskSize(75);
    .speed(7200)       diskSpeed(7200);
    .sata()            diskInterface(SATA);
  .end();
函数序列
computer();             ?? 一系列函数(功能)调用
   processor();
                        ?? 调用全局函数可能比较不便
     cores(2);
                        ?? 全局的解析状态
 processorType(i386);
                        ?? 需要上下文变量
   disk();
     diskSize(150);
   disk();
     diskSize(75);
     diskSpeed(7200);

 diskInterface(SATA);
方法链
computer()
                    ??
  .processor()
    .cores(2)       ??
    .i386()
  .disk()                ??
    .size(150)
  .disk()
    .size(75)       ??
    .speed(7200)
    .sata()         ??
  .end();
嵌套函数
computer(
     processor(        ??
       cores(2),
                            ??
 Processor.Type.i386   ??        size(75)
     ),
                       ??
     disk(
       size(150)       ??
     ),
     disk(
       size(75),
       speed(7200),

 Disk.Interface.SATA
     )
   );
DSL最佳实践
?? 启发式思考最理想的结果
 –? 你将会站在问题域的角度进行思考
 –? 引出更好的DSL
?? 朝着理想结果不断重构
?? 善假于物(Rake例子)
测试,测试,再测试!
?? 编写DSL是一件复杂的事情
?? 使用DSL则应该简单
 –? 否则你肯定犯错了
?? 测试所有的细节部分
?? 3种类型的测试
 –? 测试模型本身
 –? 测试DSL到模型的转换
 –? 测试使用DSL的特定脚本
?? 非常重要,因为你将来会进行修改
问题域
??   尽可能保持其精简
??   不要试图覆盖完整的问题域
??   推荐使用一组更为特定的DSL
??   利用元编程
没有银弹!
没有银弹!
?? 学习曲线
没有银弹!
?? 学习曲线
?? 设计良好的语言很难
没有银弹!
?? 学习曲线
?? 设计良好的语言很难
?? 构造成本
没有银弹!
??   学习曲线
??   设计良好的语言很难
??   构造成本
??   应用范围有限
没有银弹!
??   学习曲线
??   设计良好的语言很难
??   构造成本
??   应用范围有限
??   维护成本
没有银弹!
??   学习曲线
??   设计良好的语言很难
??   构造成本
??   应用范围有限
??   维护成本
??   可能被过度使用或者滥用
来看一个真实的例子
例子:Adorb
?? ADO Recordset Builder
?? 将.NET对象结果集转化为VB ADO
   RecordSet
?? 数据传输于页面展示


??   http://github.com/mingjin/Adorb
RecordsetClass rs = new RecordsetClass();

rs.Fields.Append("Id", DataTypeEnum.adInteger, 10, FieldAttributeEnum.adFldIsNullable,
Missing.Value);
rs.Fields.Append("Mon_Id", DataTypeEnum.adInteger, 10, FieldAttributeEnum.adFldIsNullable,
Missing.Value);
rs.Fields.Append("Reading_Date", DataTypeEnum.adDate, 10, FieldAttributeEnum.adFldIsNullable,
Missing.Value);
//…
rs.Open(Missing.Value, Missing.Value, CursorTypeEnum.adOpenStatic,
LockTypeEnum.adLockOptimistic, 1);

DataTable dt = monitoringService.ListMonitoringReadings(monId, siteId, departmentId,
maxNumberOfReadingsToReturn);

foreach (DataRow row in dt.Rows)
{
     rs.AddNew(Missing.Value, Missing.Value);
     rs.Fields[0].Value = row[0];
     rs.Fields[1].Value = row[1];
      rs.Fields[2].Value = row[2];
     //…
}

return rs;
RecordsetClass rs = new RecordsetClass();
                                                                                                "
rs.Fields.Append("Id", DataTypeEnum.adInteger, 10, FieldAttributeEnum.adFldIsNullable,
Missing.Value);
rs.Fields.Append("Mon_Id", DataTypeEnum.adInteger, 10, FieldAttributeEnum.adFldIsNullable,
Missing.Value);
rs.Fields.Append("Reading_Date", DataTypeEnum.adDate, 10, FieldAttributeEnum.adFldIsNullable,
Missing.Value);
//…
rs.Open(Missing.Value, Missing.Value, CursorTypeEnum.adOpenStatic,
LockTypeEnum.adLockOptimistic, 1);

DataTable dt = monitoringService.ListMonitoringReadings(monId, siteId, departmentId,
maxNumberOfReadingsToReturn);
                                                                                                "
foreach (DataRow row in dt.Rows)
{
     rs.AddNew(Missing.Value, Missing.Value);
     rs.Fields[0].Value = row[0];
     rs.Fields[1].Value = row[1];
      rs.Fields[2].Value = row[2];
     //…
}

return rs;
var"prodServEn4ty"="ProdServService.GetById(prodServId);"
var"builder"="new"RecordsetBuilder<Qual_ProdServEn4ty>();"
return"builder.From(prodServEn4ty).BuildHeadAddi4onWith(AddOwnerForHead)."
""""""""""""BuildRowAddi4onWith(AddOwnerForRow).Build();"
"
private"void"AddOwnerForRow(RecordsetClass"rs,"Qual_ProdServEn4ty"prodServEn4ty)"
{"
""""""""""""rs.Fields[OwnerName].Value"="GetUserNameById(prodServEn4ty.Owner);"
""""""""""""rs.Fields[OwnerId].Value"="prodServEn4ty.Owner;"
}"
"
private"sta4c"void"AddOwnerForHead(RecordsetClass"rs)"
{"
""""""""""""rs.Fields.Append(OwnerName,"DataTypeEnum.adVarWChar,"101,"
FieldA^ributeEnum.adFldIsNullable,"Missing.Value);"
""""""""""""rs.Fields.Append(OwnerId,"DataTypeEnum.adInteger,"0,"
FieldA^ributeEnum.adFldIsNullable,"Missing.Value);"
}"
var"prodServEn4ty"="ProdServService.GetById(prodServId);"
var"builder"="new"RecordsetBuilder<Qual_ProdServEn4ty>();"
return"builder.From(prodServEn4ty).BuildHeadAddi4onWith(AddOwnerForHead)."
""""""""""""BuildRowAddi4onWith(AddOwnerForRow).Build();"
"
private"void"AddOwnerForRow(RecordsetClass"rs,"Qual_ProdServEn4ty"prodServEn4ty)"
{"
""""""""""""rs.Fields[OwnerName].Value"="GetUserNameById(prodServEn4ty.Owner);"
""""""""""""rs.Fields[OwnerId].Value"="prodServEn4ty.Owner;"
}"
"
private"sta4c"void"AddOwnerForHead(RecordsetClass"rs)"
{"
""""""""""""rs.Fields.Append(OwnerName,"DataTypeEnum.adVarWChar,"101,"
FieldA^ributeEnum.adFldIsNullable,"Missing.Value);"
""""""""""""rs.Fields.Append(OwnerId,"DataTypeEnum.adInteger,"0,"
FieldA^ributeEnum.adFldIsNullable,"Missing.Value);"
}"
public"class"RecordsetBuilder<T>"{"
""""""""private"IEnumerable<T>"en44es;"
""""""""private"T"en4ty;"
""""""""private"Field[]"?elds;"
""""""""private"Ac4on<RecordsetClass>"headBuilder;"
""""""""private"Ac4on<RecordsetClass>"headAddi4onBuilder;"
""""""""private"Ac4on<RecordsetClass>"bodyBuilder;"
""""""""private"Ac4on<RecordsetClass,"T>"rowBuilder;"
""""""""private"Ac4on<RecordsetClass,"T>"rowAddi4onBuilder;"
""""""""//…"
}"
                       public RecordsetClass Build()
                       {
                               var recordset = new RecordsetClass();
                               (headBuilder ?? BuildHead)(recordset);
                               (headAdditionBuilder ?? (x => { }))(recordset);
                               (bodyBuilder ?? BuildBody)(recordset);
                               Initialise(recordset);
                               return recordset;
                       }
public"class"RecordsetBuilder<T>"{"
""""""""private"IEnumerable<T>"en44es;"
""""""""private"T"en4ty;"
""""""""private"Field[]"?elds;"
""""""""private"Ac4on<RecordsetClass>"headBuilder;"
""""""""private"Ac4on<RecordsetClass>"headAddi4onBuilder;"
""""""""private"Ac4on<RecordsetClass>"bodyBuilder;"
""""""""private"Ac4on<RecordsetClass,"T>"rowBuilder;"
""""""""private"Ac4on<RecordsetClass,"T>"rowAddi4onBuilder;"
""""""""//…"
}"
                       public RecordsetClass Build()
                       {
                               var recordset = new RecordsetClass();
               "               (headBuilder ?? BuildHead)(recordset);
                               (headAdditionBuilder ?? (x => { }))(recordset);
                               (bodyBuilder ?? BuildBody)(recordset);
                               Initialise(recordset);
                               return recordset;
                       }
public"sta4c"Field"As(this"string"str,"string"alias)""
{"
                                                                   Syntax"Sugar"
""""""""""""return"new"Field(str).As(alias);"
}"
"
public"sta4c"Field"As(this"En4tyField2"en4tyField2,"string"alias)"
{"
""""""""""""return"new"Field(en4tyField2).As(alias);"
}"


  public"sta4c"implicit"operator"Field(string"?eld)"
                                                               Implicit"
  {"
                                                              Conversion"
  """"""""""""return"new"Field(?eld);"
  }"
  "
  public"sta4c"implicit"operator"Field(En4tyField2"?eld)"
  {"
  """"""""""""return"new"Field(?eld);"
  }"
谢谢!

More Related Content

Similar to 使用顿蝉濒改善软件设计 (20)

Java DSL与动态代码生成技术的应用 (上集:DSL部分)
Java DSL与动态代码生成技术的应用 (上集:DSL部分)Java DSL与动态代码生成技术的应用 (上集:DSL部分)
Java DSL与动态代码生成技术的应用 (上集:DSL部分)
悦 温
?
Node分享 展烨
Node分享 展烨Node分享 展烨
Node分享 展烨
tb-vertical-guide
?
Linux binary Exploitation - Basic knowledge
Linux binary Exploitation - Basic knowledgeLinux binary Exploitation - Basic knowledge
Linux binary Exploitation - Basic knowledge
Angel Boy
?
R統計軟體 -安裝與使用
R統計軟體 -安裝與使用R統計軟體 -安裝與使用
R統計軟體 -安裝與使用
Person Lin
?
改善笔谤辞驳谤补尘尘别谤生活的蝉辩濒技能
改善笔谤辞驳谤补尘尘别谤生活的蝉辩濒技能改善笔谤辞驳谤补尘尘别谤生活的蝉辩濒技能
改善笔谤辞驳谤补尘尘别谤生活的蝉辩濒技能
Rack Lin
?
Spark introduction - In Chinese
Spark introduction - In ChineseSpark introduction - In Chinese
Spark introduction - In Chinese
colorant
?
開放原始碼 Ch2.4 app - oss - db (ver 1.0)
開放原始碼 Ch2.4   app - oss - db (ver 1.0)開放原始碼 Ch2.4   app - oss - db (ver 1.0)
開放原始碼 Ch2.4 app - oss - db (ver 1.0)
My own sweet home!
?
合久必分,分久必合
合久必分,分久必合合久必分,分久必合
合久必分,分久必合
Qiangning Hong
?
顿2冲苍辞诲别在淘宝的应用实践冲辫诲蹿版
顿2冲苍辞诲别在淘宝的应用实践冲辫诲蹿版顿2冲苍辞诲别在淘宝的应用实践冲辫诲蹿版
顿2冲苍辞诲别在淘宝的应用实践冲辫诲蹿版
Jackson Tian
?
前端样式开发演变之路
前端样式开发演变之路前端样式开发演变之路
前端样式开发演变之路
Zhao Lei
?
Jasmine
JasmineJasmine
Jasmine
tb-vertical-guide
?
N-layer design & development
N-layer design & developmentN-layer design & development
N-layer design & development
Xuefeng Zhang
?
狈辞诲别.箩蝉在淘宝的应用实践
狈辞诲别.箩蝉在淘宝的应用实践狈辞诲别.箩蝉在淘宝的应用实践
狈辞诲别.箩蝉在淘宝的应用实践
taobao.com
?
Introduction to big data
Introduction to big dataIntroduction to big data
Introduction to big data
邦宇 叶
?
Pegasus: Designing a Distributed Key Value System (Arch summit beijing-2016)
Pegasus: Designing a Distributed Key Value System (Arch summit beijing-2016)Pegasus: Designing a Distributed Key Value System (Arch summit beijing-2016)
Pegasus: Designing a Distributed Key Value System (Arch summit beijing-2016)
涛 吴
?
贰虫补诲补迟补那点事
贰虫补诲补迟补那点事贰虫补诲补迟补那点事
贰虫补诲补迟补那点事
freezr
?
狈辞厂蚕尝误用和常见陷阱分析
狈辞厂蚕尝误用和常见陷阱分析狈辞厂蚕尝误用和常见陷阱分析
狈辞厂蚕尝误用和常见陷阱分析
iammutex
?
Yog Framework
Yog FrameworkYog Framework
Yog Framework
fansekey
?
Java DSL与动态代码生成技术的应用 (上集:DSL部分)
Java DSL与动态代码生成技术的应用 (上集:DSL部分)Java DSL与动态代码生成技术的应用 (上集:DSL部分)
Java DSL与动态代码生成技术的应用 (上集:DSL部分)
悦 温
?
Linux binary Exploitation - Basic knowledge
Linux binary Exploitation - Basic knowledgeLinux binary Exploitation - Basic knowledge
Linux binary Exploitation - Basic knowledge
Angel Boy
?
R統計軟體 -安裝與使用
R統計軟體 -安裝與使用R統計軟體 -安裝與使用
R統計軟體 -安裝與使用
Person Lin
?
改善笔谤辞驳谤补尘尘别谤生活的蝉辩濒技能
改善笔谤辞驳谤补尘尘别谤生活的蝉辩濒技能改善笔谤辞驳谤补尘尘别谤生活的蝉辩濒技能
改善笔谤辞驳谤补尘尘别谤生活的蝉辩濒技能
Rack Lin
?
Spark introduction - In Chinese
Spark introduction - In ChineseSpark introduction - In Chinese
Spark introduction - In Chinese
colorant
?
開放原始碼 Ch2.4 app - oss - db (ver 1.0)
開放原始碼 Ch2.4   app - oss - db (ver 1.0)開放原始碼 Ch2.4   app - oss - db (ver 1.0)
開放原始碼 Ch2.4 app - oss - db (ver 1.0)
My own sweet home!
?
合久必分,分久必合
合久必分,分久必合合久必分,分久必合
合久必分,分久必合
Qiangning Hong
?
顿2冲苍辞诲别在淘宝的应用实践冲辫诲蹿版
顿2冲苍辞诲别在淘宝的应用实践冲辫诲蹿版顿2冲苍辞诲别在淘宝的应用实践冲辫诲蹿版
顿2冲苍辞诲别在淘宝的应用实践冲辫诲蹿版
Jackson Tian
?
前端样式开发演变之路
前端样式开发演变之路前端样式开发演变之路
前端样式开发演变之路
Zhao Lei
?
N-layer design & development
N-layer design & developmentN-layer design & development
N-layer design & development
Xuefeng Zhang
?
狈辞诲别.箩蝉在淘宝的应用实践
狈辞诲别.箩蝉在淘宝的应用实践狈辞诲别.箩蝉在淘宝的应用实践
狈辞诲别.箩蝉在淘宝的应用实践
taobao.com
?
Introduction to big data
Introduction to big dataIntroduction to big data
Introduction to big data
邦宇 叶
?
Pegasus: Designing a Distributed Key Value System (Arch summit beijing-2016)
Pegasus: Designing a Distributed Key Value System (Arch summit beijing-2016)Pegasus: Designing a Distributed Key Value System (Arch summit beijing-2016)
Pegasus: Designing a Distributed Key Value System (Arch summit beijing-2016)
涛 吴
?
贰虫补诲补迟补那点事
贰虫补诲补迟补那点事贰虫补诲补迟补那点事
贰虫补诲补迟补那点事
freezr
?
狈辞厂蚕尝误用和常见陷阱分析
狈辞厂蚕尝误用和常见陷阱分析狈辞厂蚕尝误用和常见陷阱分析
狈辞厂蚕尝误用和常见陷阱分析
iammutex
?
Yog Framework
Yog FrameworkYog Framework
Yog Framework
fansekey
?

使用顿蝉濒改善软件设计