今天我去了一家Cleartrip软件公司的面试。在第一轮编程中,我们需要构建一个小型银行应用程序。
在问题文件中,他们明确提到,不需要数据持久性。
这就是我自己做的事,我被拒绝了。
import java.util.Scanner;
public class CreateAccount {
int accountid;
String accountantname;
String IFSCcode;
public CreateAccount(int accountid,String accountantname,String IFSCcode){
this.accountid = accountid;
this.accountantname = accountantname;
this.IFSCcode = IFSCcode;
}
/*//adding deposit money with the balance
public double despositMoney() throws MiniumAmountDeposit{
double Currentbalance = 0.00;
Scanner scn = new Scanner(System.in);
System.out.println("please enter the deposit amount");
double Depositamount = scn.nextDouble();
Currentbalance += Depositamount ;
System.out.println("your currentbalance="+Currentbalance);
return Currentbalance;
}*/
//withdrawl money and set daily withdrawl limit
public void WithdrawMoney() throws InsufficientBalException, MiniumAmountDeposit{
double Currentbalance = 0.00;
Scanner deposit = new Scanner(System.in);
System.out.println("please enter the deposit amount");
double Depositamount = deposit.nextDouble();
Currentbalance += Depositamount ;
System.out.println("your currentbalance="+Currentbalance);
/*double Currentbalanace = despositMoney();*/
//setDaily Withdrawl limit
final double setDailyLimit = 2500.00;
Scanner withDraw = new Scanner(System.in);
System.out.println("please enter the withdraw amount");
double WithdrawMoney =withDraw.nextDouble();
if(Currentbalance < WithdrawMoney)
System.out.println("you have less amount : your current balance is="+Currentbalance);
else if (WithdrawMoney > setDailyLimit)
System.out.println("you have entered amount exceed than daily limit : your dailyLimit="+setDailyLimit);
else
Currentbalance -= WithdrawMoney;
System.out.println("your current balance is="+Currentbalance);
}
/*public void setWithdrawlLimit()throws exceedLimit{
Scanner scn = new Scanner(System.in);
System.out.println("please enter the withdraw amount");
double enterAmount = scn.nextDouble();
double DailysetLimit = 2500;
if(enterAmount>DailysetLimit)
System.out.println("you have exceed daily limit : your dailyLimit"+DailysetLimit);
}*/
public String toString(){
return "accountid="+this.accountid + "accountantname="+this.accountantname + "IFSCcode="+this.IFSCcode;
}
public static void main(String[] args){
CreateAccount account = new CreateAccount(1234455533,"samy","ICIC09");
System.out.println("you have created account : " +account);
/*
try {
account.despositMoney();
} catch (MiniumAmountDeposit e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
try {
account.WithdrawMoney();
} catch (InsufficientBalException | MiniumAmountDeposit e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*try {
account.setWithdrawlLimit();
} catch (exceedLimit e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
*/
}
}发布于 2018-02-25 05:38:50
听说你被拒绝了我很难过-这可不是什么好玩的事。我将逐行看一遍,最后给出一些一般性意见。
我的一些注释被标记为样式建议;这些建议通常意味着普遍接受的Java原则(命名约定等),因此我将试图指出我试图为某一特定样式辩护的地方,而不是给出一条一般规则。
import java.util.Scanner;
public class CreateAccount {风格:类通常应该是名词,例如帐户或AccountCreator,因为您使用它们作为对象,所以拥有“帐户”比“CreateAccount”更有意义。
int accountid;
String accountantname;
String IFSCcode;风格: Java几乎总是使用骆驼案例命名,所以accountid应该是accountID和accountantname accountantName等等。
public CreateAccount(int accountid,String accountantname,String IFSCcode){样式:方法名称应该以小写字母编辑开始:这是一个构造函数(my ),因此正确命名。非构造函数方法应以小写字母开头。
this.accountid = accountid;
this.accountantname = accountantname;
this.IFSCcode = IFSCcode;
}
/*//adding deposit money with the balance 这句话能告诉我什么?这个函数叫做depositMoney,所以我希望它能存钱--你不需要告诉我。相反,也许可以解释一下,存款金额来自用户的输入,或者返回值意味着什么
public double despositMoney() throws MiniumAmountDeposit{
double Currentbalance = 0.00;
Scanner scn = new Scanner(System.in);
System.out.println("please enter the deposit amount");
double Depositamount = scn.nextDouble();
Currentbalance += Depositamount ;
System.out.println("your currentbalance="+Currentbalance);
return Currentbalance;
}*/这个方法有点忙--如果从用户获得输入的功能是在其他地方(在一个单独的函数中),那就太好了,特别是因为您需要在多个命令中使用它。
而且,它看起来真的很像Currentbalance (currentBalance,如果我们使用Camel Case ;p)应该是一个对象变量。
在问题文件中,他们明确提到,不需要数据持久性。
但是,我认为它们的意思是,在程序执行之间不需要数据持久性(您不需要在程序关闭时将帐户数据保存到文件中,当程序再次启动时再将其读取)。在程序运行时跟踪用户的余额,并检查,例如,用户是否在帐户中提取了更多的钱,这将是更有意义的。
//withdrawl money and set daily withdrawl limit
public void WithdrawMoney() throws InsufficientBalException, MiniumAmountDeposit{
double Currentbalance = 0.00;
Scanner deposit = new Scanner(System.in);
System.out.println("please enter the deposit amount");
double Depositamount = deposit.nextDouble();
Currentbalance += Depositamount ;
System.out.println("your currentbalance="+Currentbalance);
/*double Currentbalanace = despositMoney();*/请看上面的注释-我们可能不想强迫用户在每次提款前存款。
//setDaily Withdrawl limit
final double setDailyLimit = 2500.00; 这是一个配置常量-它应该位于类的顶端,在方法之前。如果所有用户都有相同的退出限制,那么也应该声明为static。
Scanner withDraw = new Scanner(System.in);
System.out.println("please enter the withdraw amount");
double WithdrawMoney =withDraw.nextDouble();
if(Currentbalance < WithdrawMoney)
System.out.println("you have less amount : your current balance is="+Currentbalance);
else if (WithdrawMoney > setDailyLimit)
System.out.println("you have entered amount exceed than daily limit : your dailyLimit="+setDailyLimit);
else
Currentbalance -= WithdrawMoney;
System.out.println("your current balance is="+Currentbalance);
}这些支票不错,不错。
/*public void setWithdrawlLimit()throws exceedLimit{
Scanner scn = new Scanner(System.in);
System.out.println("please enter the withdraw amount");
double enterAmount = scn.nextDouble();
double DailysetLimit = 2500;
if(enterAmount>DailysetLimit)
System.out.println("you have exceed daily limit : your dailyLimit"+DailysetLimit);
}*/我不明白这个功能是干什么用的。
public String toString(){
return "accountid="+this.accountid + "accountantname="+this.accountantname + "IFSCcode="+this.IFSCcode;
}
public static void main(String[] args){
CreateAccount account = new CreateAccount(1234455533,"samy","ICIC09");
System.out.println("you have created account : " +account);
/*
try {
account.despositMoney();
} catch (MiniumAmountDeposit e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
try {
account.WithdrawMoney();
} catch (InsufficientBalException | MiniumAmountDeposit e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*try {
account.setWithdrawlLimit();
} catch (exceedLimit e) {
// TODO Auto-generated catch block
e.printStackTrace();
}通常,您不应该将自动生成的TODOs留在提交的代码中。在接受采访时,我不介意离开TODOs来解释我希望能实现但没有时间的功能。
*/
}
}这就是结束,所以,总的来说:看起来你在这个问题上有了一个坚实的开端。我看到的最大问题(按代码的重要性(我的意见)作为应用程序的一部分提交):
我对改进的最大建议是:始终提交尽可能干净的代码,尝试遵循广泛、重要的语言应该如何看上去的规则,因此在审阅者看来它是干净的,并致力于围绕代码中棘手或有趣的地方编写简短、有用的评论。
发布于 2018-02-26 12:24:44
很抱歉看到你被拒绝了。我们试着从这一点上学习。
首先,尝试在名称中保持一致,并遵循命名约定。在一个WithdrawMoney类中有一个CreateAccount方法。像您的CreateAccount这样的“命令”类通常具有与意图相匹配的方法(执行、执行、创建)。方法和参数必须以小写字母开头,IFSCcode应该是ifscCode,WithdrawMoney必须是withdrawMoney。这适用于公约和基本良好做法。
double不是处理金钱的最佳选择,你最好使用BigDecimal。但是在理想的世界中,您可以创建一个Money(unit, amount)类型。
您的代码将业务关注点与基础结构混合在一起,从命令行读取与管理帐户的代码在同一个类中没有任何作用。您应该看看一些公共模式,比如MVC。
我会介绍一个AccountsService,每个用例都有一个方法:+创建一个帐户:create():AccountId看起来很奇怪,但是由于没有创建的要求,所以我们保持简单,参见YAGNI。+存款货币:deposit(BigDecimal, AccountId):void +提款,遵守每日提款限额:withdraw(BigDecimal, AccountId):void ^DailyWithdrawLimitExceededExecption +支票余额:getBalance(AccountId):BigDecimal
此服务将用于隔离业务逻辑。另一方面,可以使用BankingApplicationCli来使用InputStream和OutputStream来调用AccountsService上的方法并打印结果。我的类将是一个带有主循环的简单类。第一步是选择或创建一个帐户,然后所有的操作都将在这个帐户上完成,就像一个ATM。
我想做一个接近DDD的开发;我会介绍一个Account,它可以从AccountsRepository中检索或保存。这个Account将为每个用例提供一种方法,并附带一些先决条件:
class Account {
public final AccountId id;
void deposit(BigDecimal amount) {
assert amount.signum()>0;
// ...
}
void withdraw(BigDecimal amount) throws DailyWithdrawLimitExceededExecption {
assert amount.signum()>0;
// ...
}
BigDecimal getBalance() {
// ...
}
}您可以看到,来自Account和AccountsService的方法是直接匹配的。服务将通过验证输入并委托给Account上的方法来执行先决条件。这是您在基础设施和业务之间进行链接的地方(存储库是基础设施,但稍后您还添加了安全性、事务、日志记录、..)
class AccountsService {
private final AccountsRepository repository;
void withdraw(BigDecimal amount, AccountId accountId) throws InvalidAmountException, DailyWithdrawLimitExceededExecption {
if ( amount.signum()<1 ) {
throw new InvalidAmountException(amount);
}
Account account = repository.get(accountId);
account.withdraw(amount);
repository.save(account);
}
BigDecimal getBalance(AccountId accountId) {
return repository.get(accountId).getBalance();
}
} 注意,我使用使用谓词的约定来更改系统,但在查询状态时看起来像getter。您可能不喜欢这种约定,但其想法是强调约定和自定义代码的重要性。
deposit和withdraw方法我想到的第一个想法是管理一个可变的BigDecimal字段。它在deposit:this.balance = balance.add(amount)和withdraw的基础上工作得很好。但是当你不得不处理每天的限制时,事情就变得更加复杂了。您可以持有日期和每日提取金额,但另一种方法是维护具有时间和金额的List of Operation,deposit将添加new Operation(amount),而withdraw将添加一个new Operation(amount.negate())。
若要计算每日取款,您必须筛选当前当天的所有提取操作(金额为负数)并将它们加在一起。余额是通过通过一个加法减少所有操作来计算的。
public BigDecimal getBalance() {
return history.stream()
.map(e -> e.amount)
.reduce(BigDecimal::add)
.orElse(BigDecimal.ZERO);
}
/**
* Decrease this account balance of the given amount
* @param amount a positive amount of money to remove from this balance. NotNull, GreaterThanZero
* @throws WithdrawLimitExceededException if the daily withdraw limit is exceeded. Can be checked
* via getWithdrawAmountOfToday()
*/
public void withdraw(BigDecimal amount) throws WithdrawLimitExceededException {
assert amount.signum()>0 : "withdraw amount must be positive";
BigDecimal foreseenDailyWithdraw = getWithdrawAmountOfToday().add(amount);
if ( getDailyLimit().compareTo(foreseenDailyWithdraw)<0 ) {
throw new WithdrawLimitExceededException(getDailyLimit());
}
history.add(new Operation(amount.negate()));
}通过分离所有内容,您可以很容易地测试Account的行为和AccountsService的交互。唯一需要测试的是BakingApplicationCli。
您可以在https://github.com/gervaisb/stackexchange-codereview/tree/188306/src/main/java/q188306上找到我的解决方案。我还添加了一个AccountFactory来创建一个新的有效Account。这个人不会做很多事情,但可能会在时间上进化并孤立创造过程。我选择在getDailyLimit中添加一个Account方法,这样我们就可以想象abstract Account和许多不同限制的实现。因此,工厂是选择实施的最佳地点。
发布于 2018-02-27 11:40:41
只是其他一些东西没有提到,而且非常重要:
可破坏非原子事务的并发性的意识。特别是存款/取款是非常典型的,捕鱼时提到并发问题。Atomic<BigDecimal>
由于doubles不精确,最好使用BigDecimal:双3.1与3.1000相同,但实际上可能是3.0999994。
然而,由于其他人已经提到了几件事,我就把它留给它。为了快速改善:
serachKey这样的标识符来扩展他的代码(经常看到)。Account类,那么向它添加一个BigDecimal amount = new BigDecimal("0.00");。https://codereview.stackexchange.com/questions/188306
复制相似问题