仍然是先用oo把轮廓划出来,我们需要建模一个接口来围绕它进行组合。
因为本文是关于co的论述,那么这个接口怎样分析出来的就暂时忽略掉了:
interface Dependency{
Object getArgument(int i, Class type);;
Class verifyArgument(int i, Class type);;
Object getProperty(Object key, Class type);;
Class verifyProperty(Object key, Class type);;
}
这个Dependency接口由每个不同的组件调用,来解决依赖。如果解析失败,则抛出异常。此处,我们暂时忽略异常这个细节。
getArgument负责解析一个函数参数,组件告诉Dependency对象,我需要给第3个参数,类型为String的解析依赖。于是就调用
getArgument(2, String.class)。
getProperty负责解析一个用某个key来标识的属性。比如一个javabean的property。
那两个verify是只取得解析到的那个符合要求的组件类型,但是并不实际创建对象。
然后是Component接口。这里,为了名字简短,我们不用ComponentAdapter这么恶长的名字,直接就是Component好了。
interface Component{
Class getType();;
Object create(Dependency dep);;
Class verify(Dependency dep);;
}
getType()用来返回这个Component生成的对象的类型。
create用来创建这个对象。
verify用来保证这个对象可以被创建。
至于容器接口,再简单不过了。我们都知道pico不过是个hash table,yan的容器也差不多,虽然多几个getComponentOfType()的方法,但是大体上就是一个hash table。
interface Container{
Component getComponent(Object key);;
Component getComponentOfType(Class type);;
}
好了。oo完毕。下面来co。
首先,最简单的Component是什么?什么也不干,直接返回一个值。
class ValueComponent implements Component{
private final Object v;
public Class getType();{
return v==null?null:v.getClass();;
}
public Object create(Dependency dep);{
return v;
}
public Class verify(Dependency dep);{
return getType();;
}
}
稍微难啃点的,是构造函数和工厂方法。这两个都会调用Dependency的getArgument()来取得自己需要的参数实例。
实际上,java的reflection api里面的Method和Constructor还是有很多相似点的。
为了抽取共性,我们定义一个新的接口,叫做Function:
interface Function{
Class getReturnType();;
Class[] getParameterTypes();;
Object call(Object[] args);;
}
这里,我就不展现把Method和Constructor匹配为Function的代码了,因为应该一目了然。
我们只要知道我们现在可以有三个函数产生Function对象:
class Functions{
static Function ctor(Constructor ctor);;
static Function method(Object obj, Method mtd);;
static Function static_method(Class type, Method mtd);;
}
当然,还有一些辅助函数,
比如:
static Function ctor(Class type);;
然后是FunctionComponent。
class FunctionComponent implements Component{
private final Function f;
public Class getType();{
return f.getReturnType();;
}
public Object create(Dependency dep);{
final Class[] types = f.getParameterTypes();;
final Object[] args = new Object[types.length];
foreach(t:types);{
args[i] = dep.getArgument(i, t);;
}
return f.call(args);;
}
public Class verify(Dependency dep);{
final Class[] types = f.getParameterTypes();;
foreach(t:types);{
Class arg_type = dep.verifyArgument(i, t);;
checkTypeMatch(types[i], arg_type);;
}
return f.getReturnType();;
}
}
然后一个基本的component应该是java bean的setter了,对应pico的SetterInjectionComponentAdapter,也对应spring的bean。
class BeanComponent implements Component{
private final Class type;
public Class getType();{
return type;
}
public Object create(Dependency dep);{
Object r = createInstance();;
setJavaBeans(r,dep);;
}
public Class verify(Dependency dep);{
...
}
}
具体的实现我省略了很多。因为会调用java.beans的api,并且会有一些caching优化的考虑,但是思路上很清楚,就是对每个property调用getProperty()就是了。
好,最基本的就这么几个了(其实,bean component并不是最基本的,后面我们会看到)。
下面看看都有些什么组合规则。
1。手工指定某个参数。
class WithArgument implements Component{
private final Component parent;
private final int pos;
private final Component arg;
public Class getType();{
return parent.getType();;
}
public Object create(Dependency dep);{
return parent.create(withArg(dep););;
}
public Class verify(Dependency dep);{
return parent.verify(withArg(dep););;
}
private Dependency withArg(final Dependency dep);{
return new Dependency();{
public Object getArgument(int i, Class type);{
if(i==pos);{
checkTypeMatch(type, arg);;
return arg.create(dep);;
}
else return dep.getArgument(i, type);;
}
}
...
}
}
好,通过decorate这个Dependency对象,我们得到了手工制定某个参数的能力。
这里,我们对参数仍然用Component,而不是一个简单的Object作为这个参数的值,是因为参数本身也可能需要创建,它的依赖关系也可能需要在Dependency对象中解析。如果参数不需要创建,那么,你尽可以用ValueComponent来包装一下。
2。手工指定property的值。跟上面的代码非常类似,就是重载了getProperty()和verifyProperty()。
class WithProperty implements Component{
private final Component parent;
private final Object key;
private final Component prop;
public Class getType();{
return parent.getType();;
}
public Object create(Dependency dep);{
return parent.create(withProp(dep););;
}
public Class verify(Dependency dep);{
return parent.verify(withProp(dep););;
}
private Dependency withProp(final Dependency dep);{
return new Dependency();{
public Object getProperty(Object k, Class type);{
if(k.equals(key););{
checkTypeMatch(type, prop);;
return prop.create(dep);;
}
else return dep.getProperty(k, type);;
}
}
...
}
}
3。和很多组合子一样,map是一个相当有用的组合规则。它负责把一个Component返回的对象作一下额外的处理,transform成另外一个对象。
interface Map{
Object map(Object obj);;
}
class MapComponent implements Component{
private final Component c;
private final Map map;
public Class getType();{
return null;
}
public Object create(Dependency dep);{
return map.map(c.create(dep););;
}
public Class verify(Dependency dep);{
c.verify(dep);;
return Object.class;
}
...
}
注意,这里,因为我们无法预先知道Map这个接口返回的对象会是什么类型,所以,我们让getType()返回null来标示这是一个动态决定的组件类型。
4。比map更一般化一点的,是bind动作。所谓bind,也是根据一个Component创建的对象来决定接下来返回什么动作。不同的是,它用这个对象来产生另外一个Component,让这个Component来生成一个新对象。多说无益,让我们看代码:
interface Binder{
Component bind(Object obj);;
}
class BoundComponent implements Component{
private final Component c;
private final Binder binder;
public Class getType();{
return null;
}
public Object create(Dependency dep);{
return binder.bind(c.create(dep););.create(dep);;
}
public Class verify(Dependency dep);{
c.verify(dep);;
return Object.class;
}
...
}
这个Binder接口看似简单,但是它的存在对整个co都是生死攸关的大事。可以说,如果没有这个Binder, co就基本可以不存在了。
为什么这么说呢?因为这个binder再加上前面的那个ValueComponent代表了一种非常一般性的计算模型:monad。有一个专门的数学分支:组论,就是研究monad的。
它虽然不是放之四海皆准的计算模型,比如,有比它更为一般性的Arrow模型。但是,用它几乎可以描述我们一般所遇到的大量问题。
除了前面的几个基本组合子之外,几乎所有的组合子,如果我们愿意,都可以从这个bind推衍出来。比如上面的map,如果用简洁点的函数式语法来表述的话(原谅我还是忍不住用函数式,java的语法就象一砣一砣屎一样压得我喘不过气来)
map(a, f); = bind (a, \x->value(f(x);););;
这个代码的意思是说,你可以很轻易地把一个Map对象adapt到Binder对象,只要在bind函数里面调用:
return new ValueComponent(map.map(v););;
就行了。
后面的很多组合子,比如对一个组件生成的对象调用某个方法,设置一些java bean setter,都是从这个bind组合子衍生出来的。
好了,今天时间紧迫,到此告一段落吧。
分享到:
相关推荐
Java中的Monad设计模式提供了一种封装计算或副作用的机制,能够在无副作用的方式下管理上下文和数据流的同时链接操作。 ## 二、详细解释及实际示例 1. **实际示例**: - 考虑一个Java中Monad的现实世界示例,以...
Monad入门,简介。英文版。介绍什么是Monad,以及如何使用Monad,还有如果自己定义Monad。
csharp-probability-monad, 面向 C#的概率编程框架 概率 C#基于 C#的贝叶斯建模与推理的一元概率规划。简介一般语言( 甚至在为统计计算设计的许多语言中,比如 R ) 中,贝叶斯模型的描述通常与推理算法紧密耦合。 ...
为TypeScript设计 目的是限制由于未处理的空值导致的错误 状态 抱歉,我没有时间积极维护这个项目。 很高兴对某些人有所帮助,并感谢大家的贡献! 我并不是要寻求维护者来接任-当然,如果您想继续开发TsMonad,请...
Android-Monad的核心理念是利用Monad来管理状态和副作用,这在Android开发中尤其重要,因为Android应用程序往往需要处理复杂的用户交互、异步操作以及各种系统资源的管理。通过Monad,开发者可以更好地控制这些流程...
围绕卡尔•波普尔的三个交织世界的现实观,提出面向描述知识、预测知识和引导知识的平行哲学理念,使关于智能的哲学之研究对象从Being、Becoming到Believing,并讨论结合区块链DAO技术与范畴数学理论的可能实施途径...
通常,这样的文档会介绍如何创建和组合 Monad,以及如何在实际编程中利用它们来处理资源。例如,它可能展示了如何使用 F# 的 `async { ... }` 块或者 C# 的 `async` 函数来编写异步操作,同时使用 `try/finally` 或 ...
“reasonably-priced:RúnarBjarnason提出的价格合理的Monad的组合应用程序体系结构”指的是一个由RúnarBjarnason设计的软件架构,它使用了 Monad 这一概念,旨在创建高效且成本效益高的应用程序。这里的“价格合理...
Monad-Tune 实现了一种自动化的方法,可以自动测试和选择最佳的优化组合,减少了手动调优的工作量和复杂性。 5. **Monad-Tune 应用场景** Monad-Tune 可以应用于各种需要自动优化的场景,如科学计算、机器学习、大...
### 《从简单IO到Monad转换器》:Haskell Monad经典教程 #### 一、纯与不纯(Pure Versus Impure) 本章节通过对比纯函数与非纯函数的特性来引入Monad的概念。在Haskell中,纯函数指的是那些没有副作用的函数,即...
标题提及了两本书籍资源——"Scala程序设计(第2版).pdf"和"Scala程序设计-JAVA虚拟机多核编程实战.pdf",这表明主题聚焦在Scala编程语言以及其与Java虚拟机(JVM)多核编程的结合应用上。描述简单明了,确认了这两...
MONAD是一种用于处理组合异步操作、集合、函数等计算的通用设计模式,它能够让我们把计算表达为一系列操作步骤,而不需要关心数据的内部结构。在Swift中,MONAD主要体现在Optionals和Collections的高阶函数上。 ...
Monad-ts是一个小型库,实现了一些关键的monad以及将它们链接到JavaScript和Typescript的流(管道)中的方法。 兼容Angular 2+ 。 。 内容 介绍 所有单子 也许 列表 状态 附加工具(类和功能) 异步流 流 投 克隆...
Monad 是一个抽象的概念,它提供了一种结构化的方式来组合操作,特别是在处理可能失败或需要上下文信息的操作时。在 Swift 中,Monad 可以帮助开发者编写更清晰、更易于理解的代码。 首先,让我们来看看描述中提到...
《从简单的IO到Monad Transformers (2014)》这篇文档深入浅出地探讨了函数式编程中的核心概念——单子。单子是一种具有结构的函子,它在Haskell、F#等函数式编程语言中扮演着至关重要的角色。 在函数式编程中,单子...
OCaml 和 F# 中的 Monad 我一直在阅读名为的原始 monad 论文,作为更好地理解 monad 的我决定在 OCaml 中重新实现示例代码。 由于我也在学习 F#,我决定也将其转换为 F# 与论文的显着偏差是我使用(现在)更传统的...
对于带有概率选择和递归的编程语言,估价单体能够提供一种形式化的、基于域的方法来解释程序的行为。在本文中,作者们提出了一种新的、更全面的估价单体Z,它存在于dcpo范畴的上下文中,其对象是下确界偏序集,而...
monad.js 为 NodeJS(或任何 CommonJS 实现)提供了简单的 monadic 数据类型。 也许 也许代表一个可能存在也可能不存在的值。 当一个值或函数的结果可能会或可能不会产生有意义的东西时,这是很自然的。 传统上, ...
专为Ramda设计的Maybe monad maybe-just-maybe符合《描述的Monad代数。 它实现不仅也,,,,,,,,,并 。 Maybe monad提供了一个可能存在或可能不存在的值的容器。 Maybe.Just类型用于存在的值,而Maybe....