- 浏览: 72094 次
- 性别:
- 来自: 北京
最新评论
-
360weboy:
博主:对于你的表达思想的能力由衷的佩服。我这段时间思考的点,在 ...
一个简单例子:贫血模型or领域模型 -
huangrongwei:
说得很清楚,赞。
使用泛型Entity在hibernate中的问题 -
zhuhelong520:
测试输入字节流是否能够使用指定的字符集解码。这个可能会出现死循 ...
用Java自动检查文件的编码方式 -
zyz251314:
本人刚入门 我看了银行转账的例子 OverdraftP ...
一个简单例子:贫血模型or领域模型 -
qiemengdao:
geng2483759 写道引用这里,instance变量和i ...
用happen-before规则重新审视DCL
最近taowen同学连续发起了两起关于贫血模型和领域模型的讨论,引起了大家的广泛热烈的讨论,但是讨论(或者说是争论)的结果到底怎样,我想值得商榷。问题是大家对贫血模型和领域模型都有自己的看法,如果没有对此达到概念上的共识,那么讨论的结果应该可想而知,讨论的收获也是有的,至少知道了分歧的存在。为了使问题具有确定性,我想从一个简单例子着手,用我对贫血模型和领域模型的概念来分别实现例子。至于我的理解对与否,大家可以做评判,至少有个可以评判的标准在这。
一个例子
我要举的是一个银行转帐的例子,又是一个被用滥了的例子。但即使这个例子也不是自己想出来的,而是剽窃的<<POJOs in Action>>中的例子,原谅我可怜的想像力
。当钱从一个帐户转到另一个帐户时,转帐的金额不能超过第一个帐户的存款余额,余额总数不能变,钱只是从一个账户流向另一个帐户,因此它们必须在一个事务内完成,每次事务成功完成都要记录此次转帐事务,这是所有的规则。
贫血模型
我们首先用贫血模型来实现。所谓贫血模型就是模型对象之间存在完整的关联(可能存在多余的关联),但是对象除了get和set方外外几乎就没有其它的方法,整个对象充当的就是一个数据容器,用C语言的话来说就是一个结构体,所有的业务方法都在一个无状态的Service类中实现,Service类仅仅包含一些行为。这是Java Web程序采用的最常用开发模型,你可能采用的就是这种方法,虽然可能不知道它有个“贫血模型”的称号,这要多亏Martin Flower(这个家伙惯会发明术语!)。
包结构
在讨论具体的实现之前,我们先来看来贫血模型的包结构,以便对此有个大概的了解。
贫血模型的实现一般包括如下包:
- dao:负责持久化逻辑
- model:包含数据对象,是service操纵的对象
- service:放置所有的服务类,其中包含了所有的业务逻辑
- facade:提供对UI层访问的入口
代码实现
先看model包的两个类,Account和TransferTransaction对象,分别代表帐户和一次转账事务。由于它们不包含业务逻辑,就是一个普通的Java Bean,下面的代码省略了get和set方法。
public class Account { private String accountId; private BigDecimal balance; public Account() {} public Account(String accountId, BigDecimal balance) { this.accountId = accountId; this.balance = balance; } // getter and setter .... }
public class TransferTransaction { private Date timestamp; private String fromAccountId; private String toAccountId; private BigDecimal amount; public TransferTransaction() {} public TransferTransaction(String fromAccountId, String toAccountId, BigDecimal amount, Date timestamp) { this.fromAccountId = fromAccountId; this.toAccountId = toAccountId; this.amount = amount; this.timestamp = timestamp; } // getter and setter .... }
这两个类没什么可说的,它们就是一些数据容器。接下来看service包中TransferService接口和它的实现TransferServiceImpl。TransferService定义了转账服务的接口,TransferServiceImpl则提供了转账服务的实现。
public interface TransferService { TransferTransaction transfer(String fromAccountId, String toAccountId, BigDecimal amount) throws AccountNotExistedException, AccountUnderflowException; }
public class TransferServiceImpl implements TransferService { private AccountDAO accountDAO; private TransferTransactionDAO transferTransactionDAO; public TransferServiceImpl(AccountDAO accountDAO, TransferTransactionDAO transferTransactionDAO) { this.accountDAO = accountDAO; this.transferTransactionDAO = transferTransactionDAO; } public TransferTransaction transfer(String fromAccountId, String toAccountId, BigDecimal amount) throws AccountNotExistedException, AccountUnderflowException { Validate.isTrue(amount.compareTo(BigDecimal.ZERO) > 0); Account fromAccount = accountDAO.findAccount(fromAccountId); if (fromAccount == null) throw new AccountNotExistedException(fromAccountId); if (fromAccount.getBalance().compareTo(amount) < 0) { throw new AccountUnderflowException(fromAccount, amount); } Account toAccount = accountDAO.findAccount(toAccountId); if (toAccount == null) throw new AccountNotExistedException(toAccountId); fromAccount.setBalance(fromAccount.getBalance().subtract(amount)); toAccount.setBalance(toAccount.getBalance().add(amount)); accountDAO.updateAccount(fromAccount); // 对Hibernate来说这不是必须的 accountDAO.updateAccount(toAccount); // 对Hibernate来说这不是必须的 return transferTransactionDAO.create(fromAccountId, toAccountId, amount); } }
TransferServiceImpl类使用了AccountDAO和TranferTransactionDAO,它的transfer方法负责整个转帐操作,它首先判断转帐的金额必须大于0,然后判断fromAccountId和toAccountId是一个存在的Account的accountId,如果不存在抛AccountNotExsitedException。接着判断转帐的金额是否大于fromAccount的余额,如果是则抛AccountUnderflowException。接着分别调用fromAccount和toAccount的setBalance来更新它们的余额。最后保存到数据库并记录交易。TransferServiceImpl负责所有的业务逻辑,验证是否超额提取并更新帐户余额。一切并不复杂,对于这个例子来说,贫血模型工作得非常好!这是因为这个例子相当简单,业务逻辑也不复杂,一旦业务逻辑变得复杂,TransferServiceImpl就会膨胀。
优缺点
贫血模型的优点是很明显的:
- 被许多程序员所掌握,许多教材采用的是这种模型,对于初学者,这种模型很自然,甚至被很多人认为是java中最正统的模型。
- 它非常简单,对于并不复杂的业务(转帐业务),它工作得很好,开发起来非常迅速。它似乎也不需要对领域的充分了解,只要给出要实现功能的每一个步骤,就能实现它。
- 事务边界相当清楚,一般来说service的每个方法都可以看成一个事务,因为通常Service的每个方法对应着一个用例。(在这个例子中我使用了facade作为事务边界,后面我要讲这个是多余的)
其缺点为也是很明显的:
- 所有的业务都在service中处理,当业越来越复杂时,service会变得越来越庞大,最终难以理解和维护。
- 将所有的业务放在无状态的service中实际上是一个过程化的设计,它在组织复杂的业务存在天然的劣势,随着业务的复杂,业务会在service中多个方法间重复。
- 当添加一个新的UI时,很多业务逻辑得重新写。例如,当要提供Web Service的接口时,原先为Web界面提供的service就很难重用,导致重复的业务逻辑(在贫血模型的分层图中可以看得更清楚),如何保持业务逻辑一致是很大的挑战。
领域模型
接下来看看领域驱动模型,与贫血模型相反,领域模型要承担关键业务逻辑,业务逻辑在多个领域对象之间分配,而Service只是完成一些不适合放在模型中的业务逻辑,它是非常薄的一层,它指挥多个模型对象来完成业务功能。
包结构
领域模型的实现一般包含如下包:
- infrastructure: 代表基础设施层,一般负责对象的持久化。
- domain:代表领域层。domain包中包括两个子包,分别是model和service。model中包含模型对 象,Repository(DAO)接口。它负责关键业务逻辑。service包为一系列的领域服务,之所以需要service,按照DDD的观点,是因为领域中的某些概念本质是一些行为,并且不便放入某个模型对象中。比如转帐操作,它是一个行为,并且它涉及三个对 象,fromAccount,toAccount和TransferTransaction,将它放入任一个对象中都不好。
- application: 代表应用层,它的主要提供对UI层的统一访问接口,并作为事务界限。
代码实现
现在来看实现,照例先看model中的对象:
public class Account { private String accountId; private BigDecimal balance; private OverdraftPolicy overdraftPolicy = NoOverdraftPolicy.INSTANCE; public Account() {} public Account(String accountId, BigDecimal balance) { Validate.notEmpty(accountId); Validate.isTrue(balance == null || balance.compareTo(BigDecimal.ZERO) >= 0); this.accountId = accountId; this.balance = balance == null ? BigDecimal.ZERO : balance; } public String getAccountId() { return accountId; } public BigDecimal getBalance() { return balance; } public void debit(BigDecimal amount) throws AccountUnderflowException { Validate.isTrue(amount.compareTo(BigDecimal.ZERO) > 0); if (!overdraftPolicy.isAllowed(this, amount)) { throw new AccountUnderflowException(this, amount); } balance = balance.subtract(amount); } public void credit(BigDecimal amount) { Validate.isTrue(amount.compareTo(BigDecimal.ZERO) > 0); balance = balance.add(amount); } }
与贫血模型的区别在于Account类中包含业务方法(credit,debit),注意没有set方法,对Account的更新是通过业务方法来更新的。由于“不允许从帐户取出大于存款余额的资金”是一条重要规则,将它放在一个单独的接口OverdraftPolicy中,也提供了灵活性,当业务规则变化时,只需要改变这个实现就可以了。
TransferServiceImpl类:
public class TransferServiceImpl implements TransferService { private AccountRepository accountRepository; private TransferTransactionRepository transferTransactionRepository; public TransferServiceImpl(AccountRepository accountRepository, TransferTransactionRepository transferTransactionRepository) { this.accountRepository = accountRepository; this.transferTransactionRepository = transferTransactionRepository; } public TransferTransaction transfer(String fromAccountId, String toAccountId, BigDecimal amount) throws AccountNotExistedException, AccountUnderflowException { Account fromAccount = accountRepository.findAccount(fromAccountId); if (fromAccount == null) throw new AccountNotExistedException(fromAccountId); Account toAccount = accountRepository.findAccount(toAccountId); if (toAccount == null) throw new AccountNotExistedException(toAccountId); fromAccount.debit(amount); toAccount.credit(amount); accountRepository.updateAccount(fromAccount); // 对Hibernate来说这不是必须的 accountRepository.updateAccount(toAccount); // 对Hibernate来说这不是必须的 return transferTransactionRepository.create(fromAccountId, toAccountId, amount); } }
与贫血模型中的TransferServiceImpl相比,最主要的改变在于业务逻辑被移走了,由Account类来实现。对于这样一个简单的例子,领域模型没有太多优势,但是仍然可以看到代码的实现要简单一些。当业务变得复杂之后,领域模型的优势就体现出来了。
优缺点
其优点是:
- 领域模型采用OO设计,通过将职责分配到相应的模型对象或Service,可以很好的组织业务逻辑,当业务变得复杂时,领域模型显出巨大的优势。
- 当需要多个UI接口时,领域模型可以重用,并且业务逻辑只在领域层中出现,这使得很容易对多个UI接口保持业务逻辑的一致(从领域模型的分层图可以看得更清楚)。
其缺点是:
- 对程序员的要求较高,初学者对这种将职责分配到多个协作对象中的方式感到极不适应。
- 领域驱动建模要求对领域模型完整而透彻的了解,只给出一个用例的实现步骤是无法得到领域模型的,这需要和领域专家的充分讨论。错误的领域模型对项目的危害非常之大,而实现一个好的领域模型非常困难。
- 对于简单的软件,使用领域模型,显得有些杀鸡用牛刀了。
我的看法
这部分我将提出一些可能存在争议的问题并提出自己的看法。
软件分层
理解软件分层、明晰每层的职责对于理解领域模型以及代码实现是有好处的。软件一般分为四层,分别为表示层,应用层,领域层和基础设施层。软件领域中另外一个著名的分层是TCP/IP分层,分为应用层,运输层,网际层和网络接口层。我发现它们之间存在对应关系,见下表:
TCP/IP分层 | 软件分层 | ||
表示层 | 负责向用户显示信息。 | ||
应用层 | 负责处理特定的应用程序细节。如FTP,SMTP等协议。 | 应用层 | 定义软件可以完成的工作,指挥领域层的对象来解决问题。它不负责业务逻辑,是很薄的一层。 |
运输层 | 两台主机上的应用程序提供端到端的通信。主要包括TCP,UDP协议。 | 领域层 | 负责业务逻辑,是业务软件的核心。 |
网际层 | 处理分组在网络中的活动,例如分组的选路。主要包括IP协议。 | ||
网络接口层 | 操作系统中的设备驱动程序和计算机中对应的网络接口卡。它们一起处理与电缆(或其他任何传输媒介)的物理接口细节。 | 基础设施层 | 为上层提供通用技术能力,如消息发送,数据持久化等。 |
对于TCP/IP来说,运输层和网际层是最核心的,这也是TCP/IP名字的由来,就像领域层也是软件最核心的一层。可以看出领域模型的包结构与软件分层是一致的。在软件分层中,表示层、领域层和基础设施层都容易理解,难理解的是应用层,很容易和领域层中Service混淆。领域Service属于领域层,它需要承担部分业务概念,并且这个业务概念不易放入模型对象中。应用层服务不承担任何业务逻辑和业务概念,它只是调用领域层中的对象(服务和模型)来完成自己的功能。应用层为表示层提供接口,当UI接口改变一般也会导致应用层接口改变,也可能当UI接口很相似时应用层接口不用改变,但是领域层(包括领域服务)不能变动。例如一个应用同时提供Web接口和Web Service接口时,两者的应用层接口一般不同,这是因为Web Service的接口一般要粗一些。可以和TCP/IP的层模型进行类比,开发一个FTP程序和MSN聊天程序,它们的应用层不同,但是可以同样利用TCP/IP协议,TCP/IP协议不用变。与软件分层不同的是,当同样开发一个FTP程序时,如果只是UI接口不同,一个是命令行程序,一个是图形界面,应用层不用变(利用的都是FTP服务)。下图给出领域模型中的分层:
Repository接口属于领域层
可能有人会将Repository接口,相当于贫血模型中的DAO接口,归于基础设施层,毕竟在贫血模型中DAO是和它的实现放在一起。这就涉及Repository 接口到底和谁比较密切?应该和domain层比较密切,因为Repository接口是由domain层来定义的。用TCP/IP来类比,网际层支持标准以太网、令牌环等网络接口,支持接口是在网际层中定义的,没有在网际层定义的网络接口是不能被网际层访问的。那么为什么在贫血模型中DAO的接口没有放在model包中,这是因为贫血模型中DAO的接口是由service来定义的,但是为什么DAO接口也没有放在service包中,我无法解释,按照我的观点DAO接口放在service包中要更好一些,将DAO接口放在dao包或许有名称上对应的考虑。对于领域模型,将Repository接口放入infrastructure包中会引入包的循环依赖,Repository依赖Domain,Domain依赖Repository。然而对于贫血模型,将DAO接口放入dao包中则不会引入包循环依赖,只有service对DAO和model的依赖,而没有反方向的依赖,这也导致service包很不稳定,service又正是放置业务逻辑的地方。JDepend这个工具可以检测包的依赖关系。
贫血模型中Facade有何用?
我以前的做一个项目使用的就是贫血模型,使用了service和facade,当我们讨论service和facade有什么区别时,很少有人清楚,最终结果facade就是一个空壳,它除了将方法实现委托给相应的service方法,不做任何事,它们的接口中的方法都一样。Facade应该是主要充当远程访问的门面,这在EJB时代相当普遍,自从Rod Johson叫嚷without EJB之后,大家对EJB的热情降了很多,对许多使用贫血模型的应用程序来说,facade是没有必要的。贫血模型中的service在本质上属于应用层的东西。当然如果确实需要提供远程访问,那么远程Facade(或许叫做Remote Service更好)也是很有用的,但是它仍然属于应用层,只不过在技术层面上将它的实现委托给对应的Service。下图是贫血模型的分层:
从上面的分层可以看出贫血模型实际上相当于取消掉了领域层,因为领域层并没有包含业务逻辑。
DAO到底有没有必要?
贫血模型中的DAO或领域模型中的Repository到底有没有必要?有人认为DAO或者说Repository是充血模型的大敌,对此我无论如何也不赞同。DAO或Repository是负责持久化逻辑的,如果取消掉DAO或Repository,将持久化逻辑直接写入到model对象中,势必造成model对象承担不必要的职责。虽然现在的ORM框架已经做得很好了,持久化逻辑还是需要大量的代码,持久化逻辑的掺入会使model中的业务逻辑变得模糊。允许去掉DAO的一个必要条件就是Java的的持久化框架必须足够先进,持久化逻辑的引入不会干扰业务逻辑,我认为这在很长一段时间内将无法做到。在rails中能够将DAO去掉的原因就是rail中实现持久化逻辑的代码很简洁直观,这也与ruby的表达能力强有关系。DAO的另外一个好处隔离数据库,这可以支持多个数据库,甚至可以支持文件存储。基于DAO的这些优点,我认为,即使将来Java的持久化框架做得足够优秀,使用DAO将持久化逻辑从业务逻辑中分离开来还是十分必要的,况且它们本身就应该分离。
结束语
在这篇文章里,我使用了一个转帐例子来描述领域模型和贫血模型的不同,实现代码可以从附件中下载,我推荐你看下附件代码,这会对领域模型和贫血模型有个更清楚的认识。我谈到了软件的分层,以及贫血模型和领域模型的实现又是怎样对应到这些层上去的,最后是对DAO(或Repository)的讨论。以上只是我个人观点,如有不同意见欢迎指出。
- bankingSample.zip (26.9 KB)
- 下载次数: 2076
评论
我也认为DAO是有必要的,
毕竟DAO干的那些是和业务逻辑是没有关系的。
为什么要业务逻辑混在一起啦,
而且如果以后数据持久话发生了变化,不是领域模型都要受到影响,
我认为,只是业务逻辑发生变化,才有修改领域模型对象的必要。
二:
还有,我认为在领域模型里面的那些setXXX和getXXX和业务逻辑的方法混在一起的确不是个好注意。我的观点是领域模型里面Public一个Pojo,让那些getXXX,setXXX通过哪个Pojo去做,领域模型的方法就是业务逻辑方法和持久化方法(通过委派到领域模型的DAO的方法)
这样 领域模型= pojo + Dao + 业务逻辑方法和持久化方法
service 也就是调用多个领域模型,其实service要做的就是通过那几个领域模型来实现一个用例
pojo = bo的属性 + po的属性 ?
<div class="quote_div">
<p>第一个问题,重构能够解决部分问题,但不能解决全部问题。我承认对于这个简单的转账例子,如果添加取钱和存钱服务,一个优秀的程序员完全能够提取方
法重构来消除代码上的重复,但是请注意,这只是技术上的手段,不是领域的驱动。我曾经参加的一个项目,当我查看现有代码时,发现里面很多重复代码,由于看
完《重构》不久,十分想练手,SDM也赞同,于是让我做code
review。刚开始很兴奋,但是当消除几个明显的代码重复之后,剩下来的工作越来越举步维艰,很多逻辑相互交织在一起。虽然明显看到其中存在重复,但是
却很难使用提取方法来重构,好几次还不得不回复成原来的代码,因为重构后的方法和原来的代码存在细微的不一致。提取后的方法很少具有业务意义,它只是技术
上的手段,因此也并有因为重复代码的消除而获得更可理解的代码。对于这种过程化的类,我发现几乎仅有的重构手段就是提取方法,不管是提取到当前类,还是提
取到一个外部的工具类。最终的结果是,我的重构实质上并没有带来实质性的好处。我认为,<strong><span style="color: #ff6600;">重构需要有领域模型的引导,否则它可能走错方向</span></strong><span style="color: #000000;">。</span></p>
<p> </p>
<p>第二个问题,你认为WebUI和WebService接口的Service代码能够重用吗?两者接口的粒度不同,这会导致它们使用的DTO完全不
同,要想重用几乎是不可能的。关于我谈到的领域模型的缺点,我坦率的承认实施领域模型有很大的风险,尤其当项目中没有一个人真正懂得领域模型时候。</p>
</div>
<p> </p>
<p> </p>
<p>完全赞同,尤其是对重构代码的实际意义的分析,我们重构代码如果只要在代码的层面上去看待的话我认为那是一种错误的观念。</p>
<p> </p>
<p>对于第二个问题分析的也很正确,面向webUI和面向远程调用的Facade的接口粒度肯定是不同的。</p>
Repository使用领域的通用语言,处理所有必要的DAO,并使用领域理解的语言提供对领域模型的数据访问服务。
DAO方法是细粒度的,更接近数据库,而Repository方法的粒度粗一些,而且更接近领域。此外,一个资源库类中能注入多个DAO。
对于业务逻辑复杂一些的大项目,领域模型很有必要,但是需要项目组有个有经验、有技术的 Leader。许多做了很多年的 Java 程序员也不知道什么是贫血模型,什么是领域模型。对于 DAO 和 Repository 的区别,我还是不太明白,还要到看看 DDD 那本书。
非常赞同,如果没有个技术出众的人,要想把领域模型分析好,设计好,非常难。大多数到最后还是一堆面向过程的编程。
比如select * from entity as e where e.name=:name and e.id=:id
写在service里面?
放在Service里面也就一句连写
如果我多个service都用这个查询呢?如果这个查询某天需要修改呢?修改所有service?bad smell。
用设计模式解决,不用我提醒了吧
我所能想到的就是把select 的逻辑放到另外一个类中
但如果放到另外一个类中,让service调用。
但如果这么做,和DAO模式又有什么本质的区别?仅仅是改变了一下命名方式?
多个service复用查询,可以考虑 模板设计模式嘛!
哪个查询某天要修改,Adapter不可以吗?
你这是简单问题复杂化。
比如select * from entity as e where e.name=:name and e.id=:id
写在service里面?
放在Service里面也就一句连写
如果我多个service都用这个查询呢?如果这个查询某天需要修改呢?修改所有service?bad smell。
用设计模式解决,不用我提醒了吧
我所能想到的就是把select 的逻辑放到另外一个类中
但如果放到另外一个类中,让service调用。
但如果这么做,和DAO模式又有什么本质的区别?仅仅是改变了一下命名方式?
多个service复用查询,可以考虑 模板设计模式嘛!
哪个查询某天要修改,Adapter不可以吗?
比如select * from entity as e where e.name=:name and e.id=:id
写在service里面?
放在Service里面也就一句连写
如果我多个service都用这个查询呢?如果这个查询某天需要修改呢?修改所有service?bad smell。
用设计模式解决,不用我提醒了吧
我所能想到的就是把select 的逻辑放到另外一个类中
但如果放到另外一个类中,让service调用。
但如果这么做,和DAO模式又有什么本质的区别?仅仅是改变了一下命名方式?
个人觉得数据容器还是要和行为分开的,方便扩展。
对于DAO有没有必要 我觉得不是领域应该考虑的问题,DAO是数据通道对象,这个数据通道会指向谁,还有没有逻辑这些都应该由DAO负责。DAO在我看来就算数据库的试图一样,你不应该关心数据是怎么来的,你应该关心的是这个数据是不是你需要的。DAO不是简单的数据库操作的 接口,如果这么认为那么我觉得对DAO的认识太肤浅了。
接口和接口之间,层和层之间的解偶就算一个层不应该关心另一个层的工作。层和层之间的组合协调是由纵向架构去关注的。我以前有说过,现在的开发人员大多关心横向分层却不关心纵向分层。业务的关系我觉得就是纵向的关系。
对于 DAO 和 Repository 的区别,我还是不太明白,还要到看看 DDD 那本书。
回过头来想一想分层的目的吧。是为了什么才做的分层。别忘了本阿。
正在兴起?? 面向对象数据库早就基本死掉了
看这个: http://www.infoq.com/cn/news/2008/11/Database-Martin-Fowler
同意。 respository 应该尽量淡化基本存储逻辑,而主要负责查询(find,query*)
发表评论
-
下载Project Euler题目
2008-12-10 16:00 2142就要放寒假了,总要给自己找点事做,于是想到要将projecte ... -
答复: 要领域模型干嘛?
2008-11-28 01:28 2008robbin 写道 这个东西和 ... -
从百度的一道笔试题说线程安全
2008-11-24 10:56 3674合租的室友在帮他的一 ... -
使用泛型Entity在hibernate中的问题
2008-11-20 19:16 5248在hibernate中,如果某个Entity的属性使用泛型,必 ... -
排列(递归与非递归)算法及接口设计
2008-11-06 12:17 2166本文我将首先用递归和非递归算法实现对整数数组的全排列,然后通过 ... -
用happen-before规则重新审视DCL
2008-10-31 14:28 4191编写Java多线程程序一直以来都是一件十分困难的事,多线程程序 ... -
用Java自动检查文件的编码方式
2008-10-19 11:41 8091大多数文本编辑器在打开文件时都能够自动检测文件的编码,那它是怎 ... -
一个正则表达式的测试工具
2008-10-06 20:37 2108用Java写的一个正则表达 ... -
算法导论中算法的java实现
2008-09-29 16:00 4153用java实现了算法导论中从第1章到26章的算法。 -
Java HotSpot 性能引擎架构
2008-07-16 13:54 11525原文:HotSpot Engine Architecture ... -
自定义Netbeans5生成的build.xml
2008-07-11 22:29 4520最近要做一个Swing的程 ... -
对Groovy的SimpleTemplateEngine的一点改进
2008-04-12 21:28 2557Groovy的SimpleTemplateEngine使得我们 ...
相关推荐
Chart Control 例子:所有 35 种基本图表例子。 开发环境:Visual Studio 2010 开发平台:.Net Framework 4.0 代码语言:C Sharp, Visual Basic 参考代码下载地址:http://code.msdn.microsoft.com/mschart 把注释...
【标题】中的“讯飞星火...对于AI和自然语言处理领域的初学者,这将是一个宝贵的资源,帮助他们理解大型模型的工作原理和实践应用。同时,对于经验丰富的开发者,这些代码可以帮助他们快速集成星火模型,提高开发效率。
该程序是我写的博客“一起talk C栗子吧(第一百三十回:C语言实例--C程序内存布局二)”的配套程序,共享给大家使用
中文大规模多模态预训练模型M6是一种基于transformer架构的多模态预训练模型,旨在解决中文多模态理解和生成任务。该模型具有以下特点: 1. 大规模参数:M6模型具有0.3B、10B、100B和1T四种规模的参数,满足不同...
这个"领域驱动设计模型例子-Java.rar"压缩包很可能包含了一个实际的Java项目,用于演示如何在实践中应用DDD原则和模式。 在DDD中,主要概念包括: 1. 领域模型:这是DDD的核心,由业务实体(Entities)、值对象...
接下来,我们要介绍如何使用TensorFlow构建一个简单的卷积神经网络(CNN)模型。CNN是图像识别任务的首选模型,因为它们能够捕捉图像的局部和全局特征。模型通常由多层构成,包括卷积层、池化层、全连接层和激活函数等...
【标题】"简单的OSG模型移动的例子"是一个关于OpenSceneGraph(OSG)的实践教程,旨在展示如何在3D环境中实现模型的动态移动。OpenSceneGraph是一个高性能的开源3D图形工具包,广泛用于科学可视化、游戏开发以及虚拟...
在标准的RNN中,这个重复的模块只有一个非常简单的结构,例如一个tanh层。 LSTM中的重复模块包含四个交互的层,以一种非常特殊的方式进行交互。不同于单一神经网络层,这里是有四个,以一种非常特殊的方式进行交互...
5. **数据库上下文(Database Context)**:如果涉及数据库操作,可能会有一个`DbContext`子类,如`ApplicationDbContext.cs`,继承自`DbContext`,定义模型与数据库表的映射。 通过这个简单的MVC模型例子,你可以...
- 在一个例子中,如果一个三角形的两边和一个夹角分别等于另一个三角形的相应部分,我们可以应用比例模型来计算未知三角形的面积。 - 另一个例子展示了如何利用梯形的性质和对角线的分割来找到暗影部分的面积。 - ...
2. **聚合**:聚合是DDD中的一个关键概念,它是一组相关对象的集合,有一个根元素,即聚合根,其他对象被称为聚合内的实体或值对象。聚合保持其内部的一致性,对外界提供统一的访问接口。例如,在订单管理中,订单是...
本压缩包提供了一个关于云模型的MATLAB程序设计实例,对于理解云模型及其在MATLAB中的实现具有很大的帮助。 1. **云模型基础**: 云模型由美国学者E.T. Lee提出,它结合了概率论、模糊集合理论以及随机过程,用以...
通过`osg::Group`对象,可以将多个模型组合在一起形成复杂的场景。加载模型后,将其添加到场景图中,然后设置相机视角和光照等参数,最后通过`osgViewer::Viewer`进行渲染和显示。 6. **优化和性能** 对于大型或...
在给定的博客链接(由于此处无法直接访问外部链接,但通常这类博客会详细解释示例代码),作者可能会提供一个使用C或C++实现的Select模型例子。在这样的例子中,通常会包含以下步骤: 1. 初始化:创建套接字,绑定...
完整训练代码,对应文章:【AI大模型应用开发】【Fine-Tuning】0. 从一个例子开始学习大模型Fine-Tuning
3. family例子:家庭关系的建模是一个常见的应用。可以定义“人”类,然后有“父亲”、“母亲”、“孩子”等角色。此外,人可能有属性如“年龄”、“性别”等。通过这些关系,可以描述家庭成员间的联系,进行家庭...
ActiveMQ_这里只是演示了两个简单的例子,队列模型和主题模型,另外还包含了一个服务,需要开启才_ActiveMQ