多态, DDD如何体现


Account类有withdraw, deposit两个方法,是放在Account类的里面还是外面?
方案一:
Account代表我的账户,代表我的一个身份,那么当然是一个主动的对象。
Account取钱,存钱是里所当然的。withdraw, deposit两个方法应该是Account的方法。调用方法:


account.withdraw(money)
account.deposit(money)

方案二:
Account就是户头,就是一个被动的金额数据记录。
User每次申请银行管理机构(出纳),AccountManager来操作这个Account。
调用方法:

AccountManager.withdraw(account, money)
AccountManager.deposit(account, money)

如果只存在一种Account类型的情况下(比如只有银行柜台账户),那么编程上没有根本的区别。只是这个加钱、减钱的动作的位置不同 -- 是在Account里面做,还是在AccountManager里面做。

在有多个Account类型的情况下,那情况就大不一样了。
比如,有电子信用卡远程帐户 ECardAccount,有柜台存折账户PaperAccount。这两种账户的业务规则都是不同的。
比如,电子信用卡远程帐户的取钱,要收取一定比率的手续费,而柜台存折账户就不需要。

这个时候,两种划分方法的编程上的优劣,就体现出来了。
按照第一种方案,只要为相同的Account接口,实现两个不同的类,ECardAccount,PaperAccount,分别实现不同的withdraw,deposit就可以了。
第二种方法,就有些麻烦了。需要在AccountManager里面用一个 if else,或者switch来判断Account的类型,是ECard信用卡,还是Paper存折。

这两个方法都涉及到数据库的操作。

各位,如果是DDD,怎么处理?


当然不能用第二个方案,这样,业务逻辑跑到Service等组件层去了。

在第一个方案基础上,使用桥模式,以便能够对新的存钱和取钱进行拓展。为了防止Account臃肿,可以再使用Proxy模式专门做一个AccountProxy,由其代理专门负责处理存钱和取钱方面的事务。就象歌星专门有一个经纪人代理人负责演出一样。

顶。

有人提供了一种解决方案. 我稍为整理一下,思路上很接近banq.


以下是代码(C#)


public interface IAccountAct
{
Account getAccount();
void setAccount(Account account);
}

interface IDeposite : IAccountAct
{
void deposite(double money);
}
interface IWithdraw : IAccountAct
{
void withdraw(double money);
}
interface AccountFacade
{
void doWithdraw(Account acount, double money);
void doDeposite(Account acount, double money);
}


[该贴被willem于2008-10-16 09:20修改过]




public class Account
{
private int accountType;

public Account(int accountType)
{
this.accountType = accountType;
}
public void reduceMoney(double money)
{
}
public void addMoney(double money)
{
}
public int getAccountType()
{
return accountType;
}
public void setAccountType(int accountType)
{
this.accountType = accountType;
}
}
public class AccountAct : IAccountAct
{

protected Account account;

public Account getAccount()
{
return account;
}
public void setAccount(Account account)
{
this.account = account;
}
}
class AccountFacadeImpl : AccountFacade
{
public void doWithdraw(Account account, double money)
{
IWithdraw act = FindActImpl.findIWithDrawImpl(account.getAccountType());
act.setAccount(account);
act.withdraw(money);
}
public void doDeposite(Account account, double money)
{
IDeposite act = FindActImpl.findIDepositeImpl(account.getAccountType());
act.setAccount(account);
act.deposite(money);
}
}

//实现查找
class FindActImpl
{
public static IDeposite findIDepositeImpl(int accountType)
{
switch (accountType)
{
case 0:
return new VipDeposite();
case 1:
return new NormalDeposite();
default:
throw new Exception(
"not exist");
}
}

public static IWithdraw findIWithDrawImpl(int accountType)
{
switch (accountType)
{
case 0:
return new VipWithdraw();
case 1:
return new NormalWithdraw();
default:
throw new Exception(
"not exist");
}
}
}



class NormalDeposite : AccountAct, IDeposite
{
public void deposite(double money)
{
account.addMoney(money);
//下一步,进行持久化
Console.WriteLine(
"我是普通账户,我存了{0}元", money);
}
}
class NormalWithdraw : AccountAct, IWithdraw
{
public void withdraw(double money)
{
account.reduceMoney(money);
//下一步,进行持久化
Console.WriteLine(
"我是普通账户,我取了{0}元", money);
}
}

class VipDeposite : AccountAct, IDeposite
{
public void deposite(double money)
{
account.addMoney(money);
//下一步,进行持久化
Console.WriteLine(
"我是VIP账户,我存了{0}元", money);
}
}

class VipWithdraw : AccountAct, IWithdraw
{
public void withdraw(double money)
{
account.reduceMoney(money);
//只对属性的操作
//下一步,进行持久化
Console.WriteLine(
"我是VIP账户,我取了{0}元", money);

}
}

不错,是第一个方案

但是好像比较琐碎,复杂了,建议用桥模式重构一下,不用那么多琐碎的接口和类。在具体客户端调用时,再进行组合,也就是在AccountManager中进行NormalDeposite和VipWithdraw的组合,不必事先组合这些子对象,有可能还和权限角色有关,所以,是一个动态结果。

由于需求不全面了解,以上只是一点意见,供参考。

这里想不出,如何用桥模式,我想如果要用桥模式,应该是以下图的模型,才不至于组合抽象和行为时,出现不恰当的组合,Vip账户可以取款和存款,普通用户也可以存款和取款, 但是VIP和普通用户的帐号取款和存款的逻辑不一样的.


桥模式使用的基础是把抽象共同部分和行为共同部分各自独立开来
根据您说的需求 vip和普通用户的操作是不同的 也就是没用共同的部分。存取款 取决于用户的类型,好象不适合使用桥模式。

暂时抛弃取款业务,先用存负数代替。
账号类型关系着业务方案,两者关联紧密。


interface AccountBusiness{
void deposit(Account account,Money money);
}
class DepositBusiness implements AccountBusiness{
public void deposit(Account account,Money money){
//invoke me with account and money
}
}
class Money{
public double money=0;
public Date time=new Date();
public Money(double money){
this.money=money;
}
}
abstract class Account{
abstract AccountBusiness findBusiness();
protected void deposit(Money money){
business.deposit(this,money);
}
}
class CardAccount extends Account{
public AccountBusiness findBusiness(){
//DepositBusiness business;
//search business from jndi
return business;
}
}

class TestAccountBusiness{
@Test
void testMe(){
Account cardAccount=new CardAccount();
cardAccount.deposit(new Money(-100));
}
}

先说说需求 三个方面:
不同的用户(普通用户 vip用户等)
不同的交易方式(柜台交易 网上交易等)
不同的交易动作(取款和存款)
根据需求对上面的情况进行排列组合就有八种情况 而这八种情况的业务逻辑是完全不同的(不知道理解有误吗?)

例如: 如果能将vip取款和普通用户取款的业务逻辑中 不同的地方和相同的地方提取出来 然后将不同的地方以参数的方式传递,用桥模式还是不错的
如果无法提取 用工厂模式
个人观点:)
[该贴被willer于2008-10-17 10:01修改过]

高手多多~~观望中。。。。