请高手帮忙:本人练习了一个oo的小例子,感觉有很多地方不足 请指点。

dhrubber 2006-10-25 08:24:26

两个类, 这两个类中一个表示普通银行账户(简称 P) 一个表示金卡银行账户(简称 J)。 P为基类,J继承自P。J每次最多能取5000元,P一次最多3000元。J可以透支1万元,而P不能透支。两种账户均包括账面金额字段,存钱 取钱的方法。
用户测试两种账户的存钱 取钱功能。

class GeneralUser //普通用户类
{
//账面金额,因为要在子类中使用所以定义为保护类型
protected int accountNum = 0;

public GeneralUser()
{

}
//存钱方法,子类继承此方法,存钱和取钱的行为一样 所以用同一个方法就能解决问题,这应该没有问题吧?
public int InAccount(int inAccount)
{
this.accountNum+=inAccount;
return 0;
}
//取钱方法,因为P卡和J卡的取钱规则不同 所以在这定义一个虚方法,在子类中重写了该取钱方法
public virtual int OutAccount(int outAccount)
{
if(outAccount<=3000&&outAccount<=this.accountNum)
{
this.accountNum-=outAccount;
return 0;
}
else
{
Console.WriteLine("操作不符,请重来");
return -1;
}
}

//返回卡里余额的方法,子类继承次方法
public int Balance()
{
return accountNum;
}

}

//金卡用户类 继承普通卡用户类
class SuperUser : GeneralUser
{
public SuperUser(): base()
{

}
//金卡取钱方法 重写了基类的同名方法
public override int OutAccount(int money)
{
if(money<=5000)
{
if(this.accountNum+money<-10000)
return -2;
base.accountNum-=money;
return 0;
}
else
{
Console.WriteLine("操作不符,请重来");
return -1;
}

}

}

//取钱的方法
static int getMoney(GeneralUser user)
{
user.OutAccount(3000);
return 0;
}

static void Main(string[] args)
{

//实例化一个普通用户
GeneralUser g=new GeneralUser();
g.InAccount(1000);//存入1000元
getMoney(g);//取3000元,条件不符 报错
Console.WriteLine(g.Balance());//显示卡中的余额

g=new SuperUser();//实例化一个金卡用户
g.InAccount(2000);//存入2000
getMoney(g);//取出3000,由于金卡可以透支 所以 操作正常
Console.WriteLine(g.Balance());//显示卡内透支的金额
}

我感觉在用户取钱这个环节 还是有点别扭 但想不出来怎么解决,请高人予以答复。
...全文
902 48 打赏 收藏 举报
写回复
48 条回复
切换为时间正序
请发表友善的回复…
发表回复
qltouming 2006-10-30
  • 打赏
  • 举报
回复
听aafshzj(breakthrough) 讲解,很受教益~
lxwin01 2006-10-30
  • 打赏
  • 举报
回复
没有太仔细看你的实现.
不管是普通用户还是其它用户,你应该抽象它们的共性,它们都是用户,而不是一味的继承普通用户,你可以写一个接口,让P,J去实现此接口,尽量做到针对接口编程
interface IUser
..
P:IUser
J:IUser

或者
interface IUser

abstract User
P:User
J:User

调用都是针对IUser来调用.
后者更为灵活,因为User可以实现了部分功能,或者不实现,修改影响较小,
比如IUser还有一个公共的方法(行为)没有加上,当时没有考虑周到,你只需更新IUser接口和User抽象类,其它的子类无需改动,如果不是公共的方法,子类添加也不会影响到IUser接口调用.

hero4u 2006-10-30
  • 打赏
  • 举报
回复
这个是我写的,我的思路是这样的:如果是想使用继承的话还是把父类以及公共方法都抽象化了。抽象类里面声明子类的公共属性和虚方法。然后在子类的构造方法中初始化公共属性,覆盖父类的虚方法。
你的实例中的对象都有这样有三个属性:余额,透支下限,操作最大额 两个方法存款()取款。分析清楚了后面就容易搞了。呵呵,希望能拿点分了

using System;

namespace depositloan
{
public abstract class CommonCard
{
private double mybalance;
private double mylowbound;
private double mylimited;
/// <summary>
/// 虚方法 存款(多少)
/// </summary>
/// <param name="inAccount">要存款额度</param>
/// <returns>true成功,false失败</returns>
public abstract bool deposit(double inAccount);
/// <summary>
/// 虚方法 取款(多少)
/// </summary>
/// <param name="outAccount">要取款的额度</param>
/// <returns>true成功,false失败</returns>
public abstract bool loan(double outAccount);
/// <summary>
/// 初始化余额属性
/// </summary>
public double balance
{
get
{
return mybalance;
}
set
{
mybalance = value;
}
}
/// <summary>
/// 初始化透支下限属性
/// </summary>
public double lowerbound
{
get
{
return mylowbound;
}
set
{
mylowbound = value;
}
}
/// <summary>
/// 初始化操作上限属性
/// </summary>
public double operalimit
{
get
{
return mylimited;
}
set
{
mylimited = value;
}
}
}
public class GoldCard : CommonCard
{
public GoldCard()
{
balance = 10000;
lowerbound = -10000;
operalimit = 5000;
}
public GoldCard(double balance,double lowerbound,double operalimit)
{
this.balance=balance;
this.lowerbound=lowerbound;
this.operalimit=operalimit;
}
public override bool deposit(double inAccount)
{
balance+=inAccount;
return true;
}
public override bool loan(double outAccount)
{
if(outAccount <= operalimit)
if(balance-outAccount > lowerbound)
{
balance -= outAccount;
return true;
}
return false;
}
}
/// <summary>
/// 普通卡
/// </summary>
public class card : CommonCard
{
public card()
{
balance = 3000;
lowerbound = 0;
operalimit = 3000;
}
public card(double balance,double lowerbound,double operalimit)
{
this.balance=balance;
this.lowerbound=lowerbound;
this.operalimit=operalimit;
}
public override bool deposit(double inAccount)
{
balance+=inAccount;
return true;
}
public override bool loan(double outAccount)
{
if(outAccount <= operalimit)
if(balance-outAccount > lowerbound)
{
balance -= outAccount;
return true;
}
return false;
}
}

class test
{
[STAThread]
static void Main(string[] args)
{
bool flag = true;
card mycard1 = new card();
mycard1.deposit(3000);
Console.WriteLine(mycard1.balance);
flag = mycard1.loan(1000);
if(flag)
{
Console.WriteLine("取款成功!当前帐户余额为{0}",mycard1.balance);
}
else
{
Console.WriteLine("取款失败!当前帐户余额为{0}, 帐户透支额度为{1}, 一次取款最大额为{2}",mycard1.balance,mycard1.lowerbound,mycard1.operalimit);
}
card mycard2 = new card(1500,-2000,30);
mycard2.deposit(3000);
Console.WriteLine(mycard2.balance);
flag = mycard2.loan(1000);
if(flag)
{
Console.WriteLine("取款成功!当前帐户余额为{0}",mycard2.balance);
}
else
{
Console.WriteLine("取款失败!当前帐户余额为{0}, 帐户透支额度为{1}, 一次取款最大额为{2}",mycard2.balance,mycard2.lowerbound,mycard2.operalimit);
}
// GoldCard gold1 = new GoldCard();
// GoldCard gold2 = new GoldCard(50000,-20000,8000);
}
}
}
喝醉的咖啡 2006-10-30
  • 打赏
  • 举报
回复
先前没有能够仔细看,其实有aafshzj(breakthrough)这样帮助你分析讲解是很难得的。

我赞同他的观点,只是具体而言对于初学者没必要上升那么快,如他自己提到的不是一蹴而就的事情。

针对你现在的学习,我补充一点点其它的内容,你思考一下:
1、在继承、重写方面,你的代码没什么不好的,慢慢来
有两个与 oo 无关的细节要注意一下:
A. 什么时候用方法,什么时候用属性?——比如 balance() 方法
B. int 是有符号整数,那么一个“存/取”款方法是不是就够了呢?——真实应用中别忘了数据类型的符号问题。
getMoney() 方法也有点儿意思,似乎没必要。

2、扩展不着急考虑:本例是否一定需要面向对象的方式来继承:什么时候用继承?继承的目的是什么?
首先,继承的目的不是为了复用代码。在继承的父类与子类之间必须有 is 的关系,即 子类 is 父类。
本例还好,可以这么看。

但还有一种方法来实现你的设计意图,其实我看 aafshzj(breakthrough) 等朋友已经提到了,只是可能用了比较复杂的描述方式(术语解释术语,呵呵):
观察一下,每增加一种卡的类型,例如招行有各种各样的卡——MSN卡、移动卡、....——是不是就要增加一种类,并从你的 class p 来继承?
那样的话,再看看你每增加的一种卡都有什么不同?——重写了几个方法?
再看看重写的部分究竟是什么地方变化了,这些变化能不能通过某种策略来描述,而不必新建议个类并继承?

比如,设计一个游戏系统,里面的人物和物品的设计有两种方式:
方法一:
基类:玩家
子类:战士、巫师、精灵,以后要增加不同的角色的可能性不大,因此可以考虑这三个类继承 Player 类

方法二:
基类:物品
子类:宝物、食物、装备、资源物品
子类的子类:
包子类、水果类、肉类(怪的肉)等等继承于食物
耳环类、上衣类、裤子类、甲胄类等等继承于装备
加魔法类食物,加力气类宝物、有毒类食物等等...

到这里你会发现,如果继续按照方法一的方式不停的继承,后果是什么。
那么如何修改和调整呢?
其实无论是香蕉苹果还是包子馒头,你可以把他们看作是食物类,或者根据需要进一步细分是水果类的实例——也就是说,基本属性和行为相同,仅仅是取值不同——而取值不同,已经会影响到行为的结果不同。

到此,不同的卡什么不同?透支?取款上限?这些实行为的不同、属性种类的不同,还是仅仅是属性值的不同?

所以上面有高人提醒你不要为了 oo 而 oo,大概就是这个意思了。

不过,你本例的目的是在学习语言为 OO 提供的一些基本特性,练熟悉了再来考虑这一问题也不迟。

不要着急,慢慢来,现在挺好的。
喝醉的咖啡 2006-10-30
  • 打赏
  • 举报
回复
显式/隐含使用 this 将:
1、如果子类自己覆盖了基类的方法时,调用子类重写的版本
2、子类自己没有覆盖了基类的方法时,找不到子类重新的版本,自动调用基类的版本

显式使用 base 将:
无论如何调用基类的版本。
dhrubber 2006-10-28
  • 打赏
  • 举报
回复
to: piggybank(吞硬币的小猪)

如果子类覆盖了基类的方法时,this 和 base 的区别很重要。
不写,默认是 this,而如果没有覆盖基类的方法的情况下,this 才等同于 base

++++++++++++++++++++++++
当子类覆盖基类方法时 this 和base有什么区别?
zanbuhui 2006-10-26
  • 打赏
  • 举报
回复
写的太功利了,这样的例子用来练习编码习惯更好些,比如边界检查、异常处理、命名方式等等。。
winner2050 2006-10-26
  • 打赏
  • 举报
回复
感觉为了OO而OO
iclife 2006-10-26
  • 打赏
  • 举报
回复
没时间看 先MARK
喝醉的咖啡 2006-10-26
  • 打赏
  • 举报
回复
〉没什么区别,编译的时候实际上是用base。
也不尽然。

如果子类覆盖了基类的方法时,this 和 base 的区别很重要。
不写,默认是 this,而如果没有覆盖基类的方法的情况下,this 才等同于 base

这一点在 Page Controller 这类应用模式中比较有体现。
长江支流 2006-10-26
  • 打赏
  • 举报
回复
public int static GetOutAccountMax(GeneralUser user)
{
int max = 0;

//从数据库取得max或直接在此分情况给出都行
if (user is SuperUser)
{
max = 5000;
}
else if (user is YingUser)
{
max = 4000;
}
else if (user is TongUser)
{
max = 3500;
}
else
{
max = 3000;
}
SuperUser : GeneralUser

return max;

}


SuperUser
长江支流 2006-10-26
  • 打赏
  • 举报
回复
按照上面的方法,应是楼主最容易理解的
但是,一旦又出来个银卡、铜卡...卡之类的,是不是都要重写的

解决方法可用设计模式,用下面这个吧,不要说的太多理论了,实践

public int OutAccount(int outAccount)
{
//把类作为参数,让GetOutAccountMax统一返回最大值
if(outAccount<=GetOutAccountMax(this)&&outAccount<=this.accountNum)
{
this.accountNum-=outAccount;
return 0;
}
else
{
Console.WriteLine("操作不符,请重来");
return -1;
}
}

这样,所以子类都无需重写OutAccount及相关方法,而只要在一个专门的类中定义GetOutAccountMax(用户基类参数)。

public int static GetOutAccountMax(GeneralUser user)
{
//在这里分情况返回实际用户最大取款限制

int max = 0;

//从数据库取得max或直接在此分情况给出都行



return max;

}
长江支流 2006-10-26
  • 打赏
  • 举报
回复
用户取钱这个环节,你完全可以不重写取钱方法。

你把取钱的上限作一个可重写方法就行了,返回最大可取金额限制。
因为逻辑都是一样的,所以OutAccount不要重写
public virtual int OutAccount(int outAccount)
{
if(outAccount<=GetOutAccountMax()&&outAccount<=this.accountNum)
{
this.accountNum-=outAccount;
return 0;
}
else
{
Console.WriteLine("操作不符,请重来");
return -1;
}
}

public virtual int GetOutAccountMax()
{
return 3000;//普通用户限额,当然最好定义成常数
}


你把取钱的上限作一个参数就行,子类重写返回数即可

public virtual int GetOutAccountMax()
{
return 5000;
}
aafshzj 2006-10-26
  • 打赏
  • 举报
回复
金额怎么定义为int类型?
=====================

这个我觉得不是大问题。如果我就是规定商品都是整数价格,存取也都是整数金额,也是可以的。实际上,有些企业不就是通过这样增加了一些额外收入吗?一年下来也不算很少。
Eilien 2006-10-26
  • 打赏
  • 举报
回复
mark
dhrubber 2006-10-26
  • 打赏
  • 举报
回复
金额怎么定义为int类型?

+++++++++++++++++++++
练习的 所以很多细节就没有太多加考虑
xjjdanran 2006-10-26
  • 打赏
  • 举报
回复
金额怎么定义为int类型?
dhrubber 2006-10-26
  • 打赏
  • 举报
回复
感觉为了OO而OO

+++++++++++++++++
您说的没错 而我也仅仅是为了练习。

请您提点建议
pol000 2006-10-26
  • 打赏
  • 举报
回复
mark
喝醉的咖啡 2006-10-26
  • 打赏
  • 举报
回复
dhrubber() 用这样的方式学习,每一个细节慢慢推敲,方法很好,收获也会很大——为了解决具体问题的时候另当别论,那时候显然需要功利一些

我当了半年 CS 老师,教各种 CS,呵呵
加载更多回复(28)
发帖
.NET社区

6.1w+

社区成员

.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
帖子事件
创建了帖子
2006-10-25 08:24
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。