本书兼顾Julia编程语言的优势和不足,全面介绍了Julia编程语言,介绍了模式所依赖的编程语言特性,同时涵盖了一些高级的内容。全书分为四部分,第1部分介绍设计模式,第二部分就少Julia基础,第三部分介绍设计模式实现,第四部分介绍一些进阶主题。
目录
- 序言
- 前言
- 作者简介
- 审校者简介
- 第一部分从设计模式开始
- 第1章设计模式和相关原则 2
- 1.1设计模式的历史 2
- 1.1.1设计模式的兴起 3
- 1.1.2关于GoF模式的更多思考 3
- 1.1.3在本书中我们如何描述设计模式 4
- 1.2软件设计原则 4
- 1.2.1SOLID原则 5
- 1.2.2DRY原则 6
- 1.2.3KISS原则 6
- 1.2.4POLA原则 7
- 1.2.5YAGNI原则 7
- 1.2.6POLP原则 8
- 1.3软件质量目标 8
- 1.3.1可重用性 8
- 1.3.2性能 9
- 1.3.3可维护性 10
- 1.3.4安全性 11
- 1.4小结 11
- 1.5问题 12
- 第二部分Julia基础
- 第2章模块、包和数据类型 14
- 2.1技术要求 14
- 2.2程序开发中不断增长的痛点 15
- 2.2.1数据科学项目 15
- 2.2.2企业应用程序 15
- 2.2.3适应增长 16
- 2.3使用命名空间、模块和包 16
- 2.3.1理解命名空间 17
- 2.3.2创建模块和包 17
- 2.3.3创建子模块 23
- 2.3.4在模块中管理文件 24
- 2.4管理包的依赖关系 24
- 2.4.1理解语义版本控制方案 24
- 2.4.2指定Julia包的依赖关系 25
- 2.4.3避免循环依赖 28
- 2.5设计抽象类型和具体类型 29
- 2.5.1设计抽象类型 29
- 2.5.2设计具体类型 33
- 2.5.3使用类型运算符 37
- 2.5.4抽象类型和具体类型的差异 39
- 2.6使用参数化类型 39
- 2.6.1使用参数化复合类型 40
- 2.6.2使用参数化抽象类型 42
- 2.7数据类型转换 43
- 2.7.1执行简单的数据类型转换 44
- 2.7.2注意有损转换 44
- 2.7.3理解数字类型转换 45
- 2.7.4重温自动转换规则 45
- 2.7.5理解函数分派规则 47
- 2.8小结 48
- 2.9问题 49
- 第3章设计函数和接口 50
- 3.1技术要求 50
- 3.2设计函数 51
- 3.2.1用例——太空战争游戏 51
- 3.2.2定义函数 51
- 3.2.3注释函数参数 52
- 3.2.4使用可选参数 55
- 3.2.5使用关键字参数 57
- 3.2.6接受可变数量的参数 58
- 3.2.7splatting参数 59
- 3.2.8第一类实体函数 60
- 3.2.9开发匿名函数 61
- 3.2.10使用do语法 62
- 3.3理解多重分派 63
- 3.3.1什么是分派 63
- 3.3.2匹配最窄类型 64
- 3.3.3分派多个参数 65
- 3.3.4分派过程中可能存在的歧义 67
- 3.3.5歧义检测 68
- 3.3.6理解动态分派 70
- 3.4利用参数化方法 71
- 3.4.1使用类型参数 71
- 3.4.2使用类型参数替换抽象类型 72
- 3.4.3在使用参数时强制类型一致性 73
- 3.4.4从方法签名中提取类型信息 74
- 3.5使用接口 75
- 3.5.1设计和开发接口 75
- 3.5.2处理软契约 79
- 3.5.3使用特质 80
- 3.6小结 81
- 3.7问题 81
- 第4章宏和元编程 82
- 4.1技术要求 83
- 4.2理解元编程的需求 83
- 4.2.1使用@time宏测量性能 83
- 4.2.2循环展开 84
- 4.3使用表达式 86
- 4.3.1试用解析器 86
- 4.3.2手动构造表达式对象 88
- 4.3.3尝试更复杂的表达式 90
- 4.3.4计算表达式 93
- 4.3.5在表达式中插入变量 94
- 4.3.6对符号使用QuoteNode 95
- 4.3.7在嵌套表达式中插值 96
- 4.4开发宏 97
- 4.4.1什么是宏 97
- 4.4.2编写第一个宏 98
- 4.4.3传递字面量参数 98
- 4.4.4传递表达式参数 99
- 4.4.5理解宏扩展过程 100
- 4.4.6操作表达式 101
- 4.4.7理解卫生宏 104
- 4.4.8开发非标准字符串字面量 105
- 4.5使用生成函数 107
- 4.5.1定义生成函数 108
- 4.5.2检查生成函数参数 109
- 4.6小结 110
- 4.7问题 110
- 第三部分实现设计模式
- 第5章可重用模式 114
- 5.1技术要求 114
- 5.2委托模式 114
- 5.2.1在银行用例中应用委托模式 115
- 5.2.2现实生活中的例子 119
- 5.2.3注意事项 120
- 5.3Holy Traits模式 120
- 5.3.1重温个人资产管理用例 121
- 5.3.2实现Holy Traits模式 122
- 5.3.3重温一些常见用法 126
- 5.3.4使用SimpleTraits.jl包 129
- 5.4参数化类型模式 130
- 5.4.1在股票交易应用程序中使用删除文本参数化类型 132
- 5.4.2现实生活中的例子 135
- 5.5小结 138
- 5.6问题 139
- 第6章性能模式 140
- 6.1技术要求 141
- 6.2全局常量模式 141
- 6.2.1使用全局变量对性能进行基准测试 141
- 6.2.2享受全局常量的速度 143
- 6.2.3使用类型信息注释变量 143
- 6.2.4理解常量为何有助于性能 144
- 6.2.5将全局变量作为函数参数传递 145
- 6.2.6将变量隐藏在全局常量中 145
- 6.2.7现实生活中的例子 146
- 6.2.8注意事项 147
- 6.3数组结构模式 147
- 6.3.1使用业务领域模型 148
- 6.3.2使用不同的数据布局提高性能 150
- 6.3.3注意事项 155
- 6.4共享数组模式 155
- 6.4.1风险管理用例介绍 156
- 6.4.2准备示例数据 157
- 6.4.3高性能解决方案概述 158
- 6.4.4在共享数组中填充数据 159
- 6.4.5直接在共享数组上分析数据 161
- 6.4.6理解并行处理的开销 163
- 6.4.7配置共享内存使用情况 164
- 6.4.8确保工作进程可以访问代码和数据 166
- 6.4.9避免并行进程之间的竞态 167
- 6.4.10使用共享数组的约束 167
- 6.5记忆模式 168
- 6.5.1斐波那契函数介绍 168
- 6.5.2改善斐波那契函数的性能 169
- 6.5.3自动化构造记忆缓存 171
- 6.5.4理解泛型函数的约束 172
- 6.5.5支持具有多个参数的函数 173
- 6.5.6处理参数中的可变数据类型 174
- 6.5.7使用宏来记忆泛型函数 176
- 6.5.8现实生活中的例子 177
- 6.5.9注意事项 178
- 6.5.10使用Caching.jl包 178
- 6.6闸函数模式 180
- 6.6.1识别类型不稳定的函数 181
- 6.6.2理解性能影响 182
- 6.6.3开发闸函数 183
- 6.6.4处理类型不稳定的输出变量 183
- 6.6.5使用@inferred宏 186
- 6.7小结 187
- 6.8问题 187
- 第7章可维护性模式 188
- 7.1技术要求 188
- 7.2子模块模式 189
- 7.2.1理解何时需要子模块 189
- 7.2.2理解传入耦合与传出耦合 190
- 7.2.3管理子模块 191
- 7.2.4在模块和子模块之间引用符号和函数 191
- 7.2.5删除双向耦合 193
- 7.2.6考虑拆分为顶层模块 195
- 7.2.7理解使用子模块的反论点 195
- 7.3关键字定义模式 195
- 7.3.1重温结构定义和构造函数 196
- 7.3.2在构造函数中使用关键字参数 196
- 7.3.3使用@kwdef宏简化代码 197
- 7.4代码生成模式 198
- 7.4.1文件日志记录器用例介绍 199
- 7.4.2函数定义的代码生成 201
- 7.4.3调试代码生成 202
- 7.4.4考虑代码生成以外的选项 204
- 7.5领域特定语言模式 205
- 7.5.1L系统介绍 206
- 7.5.2为L系统设计DSL 207
- 7.5.3重温L系统核心逻辑 208
- 7.5.4实现L系统的DSL 210
- 7.6小结 215
- 7.7问题 215
- 第8章鲁棒性模式 216
- 8.1技术要求 217
- 8.2访问器模式 217
- 8.2.1识别对象的隐式接口 217
- 8.2.2实现getter函数 218
- 8.2.3实现setter函数 219
- 8.2.4禁止直接访问字段 220
- 8.3属性模式 220
- 8.3.1延迟文件加载器介绍 220
- 8.3.2理解用于字段访问的点符号 222
- 8.3.3实现读取访问和延迟加载 223
- 8.3.4控制对对象字段的写入访问 226
- 8.3.5报告可访问字段 227
- 8.4let块模式 228
- 8.4.1网络爬虫用例介绍 228
- 8.4.2使用闭包将私有变量和函数隐藏起来 230
- 8.4.3限制长脚本或函数的变量范围 232
- 8.5异常处理模式 233
- 8.5.1捕捉和处理异常 233
- 8.5.2处理各种类型的异常 233
- 8.5.3在顶层处理异常 235
- 8.5.4跟随栈帧 236
- 8.5.5理解异常处理对性能的影响 238
- 8.5.6重试操作 239
- 8.5.7异常时选用nothing 241
- 8.6小结 242
- 8.7问题 243
- 第9章其他模式 244
- 9.1技术要求 244
- 9.2单例类型分派模式 245
- 9.2.1开发命令处理器 245
- 9.2.2理解单例类型 245
- 9.2.3使用Val参数化数据类型 246
- 9.2.4使用单例类型进行动态分派 247
- 9.2.5理解分派的性能优势 249
- 9.3打桩/模拟模式 251
- 9.3.1什么是测试替身 251
- 9.3.2信贷审批用例介绍 252
- 9.3.3使用打桩执行状态验证 253
- 9.3.4使用Mocking包实现打桩 255
- 9.3.5将多个打桩应用于同一函数 256
- 9.3.6使用模拟执行行为验证 257
- 9.4函数管道模式 259
- 9.4.1Hacker News分析用例介绍 260
- 9.4.2理解函数管道 264
- 9.4.3设计可组合函数 265
- 9.4.4为平均得分函数开发函数管道 266
- 9.4.5在函数管道中实现条件逻辑 269
- 9.4.6沿函数管道进行广播 270
- 9.4.7有关使用函数管道的注意事项 271
- 9.5小结 272
- 9.6问题 272
- 第10章反模式 273
- 10.1技术要求 273
- 10.2海盗反模式 274
- 10.2.1I类海盗—重新定义函数 274
- 10.2.2II类海盗—不用自己的类型扩展 275
- 10.2.3III类海盗—用自己的类型扩展,但目的不同 277
- 10.3窄参数类型反模式 279
- 10.3.1考虑参数类型的多种选项 279
- 10.3.2评估性能 284
- 10.4非具体字段类型反模式 285
- 10.4.1理解复合数据类型的内存布局 285
- 10.4.2设计复合类型时要考虑具体类型 287
- 10.4.3比较具体字段类型和非具体字段类型的性能 288
- 10.5小结 289
- 10.6问题 289
- 第11章传统的面向对象模式 290
- 11.1技术要求 290
- 11.2创建型模式 291
- 11.2.1工厂方法模式 291
- 11.2.2抽象工厂模式 292
- 11.2.3单例模式 294
- 11.2.4建造者模式 296
- 11.2.5原型模式 297
- 11.3行为型模式 298
- 11.3.1责任链模式 298
- 11.3.2中介者模式 300
- 11.3.3备忘录模式 302
- 11.3.4观察者模式 304
- 11.3.5状态模式 305
- 11.3.6策略模式 306
- 11.3.7模板方法模式 308
- 11.3.8命令模式、解释器模式、迭代器模式和访问者模式 310
- 11.4结构型模式 310
- 11.4.1适配器模式 310
- 11.4.2组合模式 313
- 11.4.3享元模式 314
- 11.4.4桥接模式、装饰器模式和外观模式 316
- 11.5小结 316
- 11.6问题 317
- 第四部分进阶主题
- 第12章继承与变体 320
- 12.1技术要求 320
- 12.2实现继承和行为子类型化 321
- 12.2.1理解实现继承 321
- 12.2.2理解行为子类型化 323
- 12.2.3正方形–矩形问题 324
- 12.2.4脆弱的基类问题 326
- 12.2.5重温鸭子类型 327
- 12.3协变、不变和逆变 328
- 12.3.1理解不同种类的变体 328
- 12.3.2参数化类型是不变的 328
- 12.3.3方法参数是协变的 331
- 12.3.4剖析函数类型 331
- 12.3.5确定函数类型的变体 333
- 12.3.6实现自己的函数类型分派 335
- 12.4再谈参数化方法 336
- 12.4.1指定类型变量 337
- 12.4.2匹配类型变量 337
- 12.4.3理解对角线规则 338
- 12.4.4对角线规则的例外 339
- 12.4.5类型变量的可用性 339
- 12.5小结 340
- 12.6问题 341
- 问题答案 342