首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >微型银行应用

微型银行应用
EN

Code Review用户
提问于 2018-02-25 04:31:42
回答 5查看 15.3K关注 0票数 2

今天我去了一家Cleartrip软件公司的面试。在第一轮编程中,我们需要构建一个小型银行应用程序。

在问题文件中,他们明确提到,不需要数据持久性。

  • 创建帐号
  • 押金
  • 提款,遵守每日提款限额。
  • 检查余额

这就是我自己做的事,我被拒绝了。

代码语言:javascript
复制
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();
        }

*/      
    }

}
EN

回答 5

Code Review用户

发布于 2018-02-25 05:38:50

听说你被拒绝了我很难过-这可不是什么好玩的事。我将逐行看一遍,最后给出一些一般性意见。

我的一些注释被标记为样式建议;这些建议通常意味着普遍接受的Java原则(命名约定等),因此我将试图指出我试图为某一特定样式辩护的地方,而不是给出一条一般规则。

代码语言:javascript
复制
import java.util.Scanner;

public class CreateAccount {

风格:类通常应该是名词,例如帐户或AccountCreator,因为您使用它们作为对象,所以拥有“帐户”比“CreateAccount”更有意义。

代码语言:javascript
复制
    int accountid;
    String accountantname;
    String IFSCcode;

风格: Java几乎总是使用骆驼案例命名,所以accountid应该是accountIDaccountantname accountantName等等。

代码语言:javascript
复制
   public CreateAccount(int accountid,String accountantname,String IFSCcode){

样式:方法名称应该以小写字母编辑开始:这是一个构造函数(my ),因此正确命名。非构造函数方法应以小写字母开头。

代码语言:javascript
复制
    this.accountid = accountid;
    this.accountantname = accountantname;
    this.IFSCcode = IFSCcode;
   }

    /*//adding deposit money with the balance 

这句话能告诉我什么?这个函数叫做depositMoney,所以我希望它能存钱--你不需要告诉我。相反,也许可以解释一下,存款金额来自用户的输入,或者返回值意味着什么

代码语言:javascript
复制
    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)应该是一个对象变量。

在问题文件中,他们明确提到,不需要数据持久性。

但是,我认为它们的意思是,在程序执行之间不需要数据持久性(您不需要在程序关闭时将帐户数据保存到文件中,当程序再次启动时再将其读取)。在程序运行时跟踪用户的余额,并检查,例如,用户是否在帐户中提取了更多的钱,这将是更有意义的。

代码语言:javascript
复制
    //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();*/

请看上面的注释-我们可能不想强迫用户在每次提款前存款。

代码语言:javascript
复制
        //setDaily Withdrawl limit
           final double setDailyLimit = 2500.00;   

这是一个配置常量-它应该位于类的顶端,在方法之前。如果所有用户都有相同的退出限制,那么也应该声明为static

代码语言:javascript
复制
            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);

    }

这些支票不错,不错。

代码语言:javascript
复制
    /*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);


    }*/

我不明白这个功能是干什么用的。

代码语言:javascript
复制
    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来解释我希望能实现但没有时间的功能。

代码语言:javascript
复制
*/      
    }
}

这就是结束,所以,总的来说:看起来你在这个问题上有了一个坚实的开端。我看到的最大问题(按代码的重要性(我的意见)作为应用程序的一部分提交):

  1. 密码看上去有点仓促。就像在面对面的面试中一样,外观对于将要阅读和评审的代码很重要。没有解释的大量注释代码,以及拼写错误/不一致的命名方案,都不会给人留下好印象。
  2. 似乎您对问题规范有点不确定。在编写代码时,仔细考虑每个函数的输入/输出,以及需要将哪些数据存储在对象变量中,这将非常有帮助--随着您进一步了解问题,这些事情可能会发生变化。这是非常有帮助的文件(写在评论!)你对程序应该如何运行所做的假设。
  3. 在某些地方,您与已建立的Java规则有很大的偏差。没有一家公司拥有完全相同的代码风格(这是Google的for Java),但是有一些东西(例如骆驼案例)对于给定的语言来说是非常重要的。编写Java时不正确地使用camel案例是一个信号,表明您不太了解Java --取决于您申请的职位,这可能是一个问题,也可能不是一个问题。

我对改进的最大建议是:始终提交尽可能干净的代码,尝试遵循广泛、重要的语言应该如何看上去的规则,因此在审阅者看来它是干净的,并致力于围绕代码中棘手或有趣的地方编写简短、有用的评论。

票数 6
EN

Code Review用户

发布于 2018-02-26 12:24:44

很抱歉看到你被拒绝了。我们试着从这一点上学习。

首先,尝试在名称中保持一致,并遵循命名约定。在一个WithdrawMoney类中有一个CreateAccount方法。像您的CreateAccount这样的“命令”类通常具有与意图相匹配的方法(执行、执行、创建)。方法和参数必须以小写字母开头,IFSCcode应该是ifscCodeWithdrawMoney必须是withdrawMoney。这适用于公约和基本良好做法。

double不是处理金钱的最佳选择,你最好使用BigDecimal。但是在理想的世界中,您可以创建一个Money(unit, amount)类型。

关注点分离

您的代码将业务关注点与基础结构混合在一起,从命令行读取与管理帐户的代码在同一个类中没有任何作用。您应该看看一些公共模式,比如MVC。

我会介绍一个AccountsService,每个用例都有一个方法:+创建一个帐户:create():AccountId看起来很奇怪,但是由于没有创建的要求,所以我们保持简单,参见YAGNI。+存款货币:deposit(BigDecimal, AccountId):void +提款,遵守每日提款限额:withdraw(BigDecimal, AccountId):void ^DailyWithdrawLimitExceededExecption +支票余额:getBalance(AccountId):BigDecimal

此服务将用于隔离业务逻辑。另一方面,可以使用BankingApplicationCli来使用InputStreamOutputStream来调用AccountsService上的方法并打印结果。我的类将是一个带有主循环的简单类。第一步是选择或创建一个帐户,然后所有的操作都将在这个帐户上完成,就像一个ATM。

建模业务概念

我想做一个接近DDD的开发;我会介绍一个Account,它可以从AccountsRepository中检索或保存。这个Account将为每个用例提供一种方法,并附带一些先决条件:

代码语言:javascript
复制
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() {
        // ...
    }
}

您可以看到,来自AccountAccountsService的方法是直接匹配的。服务将通过验证输入并委托给Account上的方法来执行先决条件。这是您在基础设施和业务之间进行链接的地方(存储库是基础设施,但稍后您还添加了安全性、事务、日志记录、..)

代码语言:javascript
复制
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。您可能不喜欢这种约定,但其想法是强调约定和自定义代码的重要性。

设计depositwithdraw方法

我想到的第一个想法是管理一个可变的BigDecimal字段。它在depositthis.balance = balance.add(amount)和withdraw的基础上工作得很好。但是当你不得不处理每天的限制时,事情就变得更加复杂了。您可以持有日期和每日提取金额,但另一种方法是维护具有时间和金额的List of Operationdeposit将添加new Operation(amount),而withdraw将添加一个new Operation(amount.negate())

若要计算每日取款,您必须筛选当前当天的所有提取操作(金额为负数)并将它们加在一起。余额是通过通过一个加法减少所有操作来计算的。

代码语言:javascript
复制
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和许多不同限制的实现。因此,工厂是选择实施的最佳地点。

票数 2
EN

Code Review用户

发布于 2018-02-27 11:40:41

只是其他一些东西没有提到,而且非常重要:

可破坏非原子事务的并发性的意识。特别是存款/取款是非常典型的,捕鱼时提到并发问题。Atomic<BigDecimal>

由于doubles不精确,最好使用BigDecimal:双3.1与3.1000相同,但实际上可能是3.0999994。

然而,由于其他人已经提到了几件事,我就把它留给它。为了快速改善:

  • 遵循java惯例,特别是w.r.t。命名:方法和变量以一个小写字母开头。
  • 将人工输入/输出与数据对象上的方法分开。
  • 注意拼写(“存款”)。没有人希望用像serachKey这样的标识符来扩展他的代码(经常看到)。
  • 考虑一下数据结构,如果一个人有一个Account类,那么向它添加一个BigDecimal amount = new BigDecimal("0.00");
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/188306

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档