2017-09-10 3 views
3

私の質問は、stdinによって初期化されるフィールドを持つJavaクラスのコンストラクタを記述する最適な方法は何ですか?私はこのクラスのすべてのセッターとゲッターを書くことができますJavaフィールドのフィールドがエンドユーザによって供給されているクラスのコンストラクタ

Public class Employee { 
    private int empID; 
    private String empName; 
    private List<Role> empRoles; 
    {....} 
} 

:たとえば

は、私がどのように見えるEmployeeクラスを持っていると仮定します。もちろん、Roleクラスには独自のファイルがあります。次のように

また、フィールドを初期化するために、エンドユーザーを有効にするために、私は最初の2つのフィールドのための私のセッターを行うこととします

public void setEmpID() { 
    System.out.println("Please enter the employee ID"); 
    Scanner s = new Scanner (System.in); 
    this.empID = s.nextInt(); 

public void setEmpName() { 
    System.out.println("Please enter the employee name"); 
    Scanner s = new Scanner (System.in); 
    this.empName = s.next(); 
} 

その後:

  1. は、私はそのような使用できますデフォルトの コンストラクタをオーバーライドするコンストラクタのsetterです。
  2. これは、このようなコンストラクタを作成する最善の方法ですか?
  3. はそれがより良いセッターのための引数としてコンストラクタに私は、各セッターに作成していScannerオブジェクトを移動して作ることです

例:

public void setEmpName(Scanner s) { 
    ... 
    this.empName = s.next(); 
} 

あなたが見ることができるように、この単に「コーディング」ではなく、設計上の疑問であるかもしれません。

ご協力いただきありがとうございます。

+1

このような状況のためのソリューションは、Builderパターンを使用することです。設定したいデータをメソッドに渡します。ファイルの一部のデータに基づいてフィールドを設定する場合はどうなりますか?またはインターネットから? 'Scanner'の使用を強制すると、あなたのクラスがどれだけ役に立つかが本当に制限されます。 – Carcigenicate

+1

setterは、メソッドの引数として設定する値を取得する必要があります。ユーザーの入力は、前に別のポイントで行う必要があります。すべてのユーザー入力を取得するために設計された指定されたメソッドまたはクラス。後で入力ソースを簡単に交換することができ、基底クラスは変更を必要とせず、それははるかにモジュール化されています。 – Zabuza

+0

多くのありがとう。それを得た:) –

答えて

4

ユーザーの入出力をプログラムモデルと混同している可能性があります。ここの鍵は、あなたが2つを完全に別々に保つべきだということです。このようにして、GUI、コンソールプログラム、または必要な場所で使用できるため、Employeeクラスでは、使用するUIまたはI/Oのタイプについて、は全くありません()。

したがって、Employeeコンストラクタは、ソースに関係なくEmployeeオブジェクトを作成するために必要なデータを取り込むだけで、フィールド取得者にも同じデータを取り込む必要があります。

だからあなたのゲッターは、あなたが投稿したように何も見ないし、代わりに、はるかに無地になり、より多くの「ダム」または「無知」のユーザのI/O(スキャナ、System.in、など)

public void getEmpID (int empID) { 
    this.empID = empID; 
} 

他のフィールドと同じです。

I/Oのすべてのもの - スキャナクラスなどは、ドライバクラスのどこかにあります。

サイドノート:System.inに基づいてスキャナーを使用する場合、プログラムはそのような唯一の獣を作成し、必要なときに作成し、プログラムが完全に完了したときに閉じて処分する必要があります。そうしないと、接続が途中で終了してシステム入力が中断される危険性があります。これは、複数のScannerオブジェクトを作成する際に提案されたコードを使用しない別の理由です。

たとえば....

import java.util.ArrayList; 
import java.util.List; 

public class Employee { 
    private int empID; 
    private String empName; 
    private List<Role> empRoles; 

    public Employee(int empID, String empName) { 
     super(); 
     this.empID = empID; 
     this.empName = empName; 
     empRoles = new ArrayList<>(); 
    } 

    public int getEmpID() { 
     return empID; 
    } 

    public void setEmpID(int empID) { 
     this.empID = empID; 
    } 

    public String getEmpName() { 
     return empName; 
    } 

    public void setEmpName(String empName) { 
     this.empName = empName; 
    } 

    public List<Role> getEmpRoles() { 
     return empRoles; 
    } 

    public boolean addEmpRole(Role role) { 
     return empRoles.add(role); 
    } 

    public boolean removeEmpRole(Role role) { 
     return empRoles.remove(role); 
    } 

} 

あなたはそのようにのような他の場所でそれを使用することができます:

import java.util.Scanner; 

public class TestEmployee { 
    public static void main(String[] args) { 
     Scanner scan = new Scanner(System.in); 

     System.out.print("Enter employee ID: "); 
     int empID = scan.nextInt(); 
     scan.nextLine(); // handle dangling end of line token 

     System.out.print("Enter employee Name: "); 
     String empName = scan.nextLine(); 
     Employee employee = new Employee(empID, empName);  

     // if we are **totally** done with the Scanner, now we may close it 
     scan.close(); 
    } 
} 
+0

本当に多くのありがとう。理にかなっている。それは設計上の問題でしたが、コンストラクタではなく、全体の "ソリューション"とそのモジュール性の問題でした。 –

+0

@HassanShahin:はい。たとえば、I/Oコードを使用した従業員コードの使用の編集を参照してください。 –

+1

@DontKnowMuchBut良くなっています非常に細かい回答もあります(+1) – davidxxx

5

は実際に、あなたは、オブジェクトのフィールドを移入するために、特定のコンストラクタが、無引数を使用する方法に依存しないんコンストラクタ。
実際にを呼び出した後で、Employeeインスタンスのフィールドに値を設定するためのセッターアプローチを選択しました。
しかし、このセッターのアプローチは、ユーザーの入力やオブジェクトの状態の設定など、あまりにも多くの作業を混在させると複雑です。

このようなセッターは、デフォルトの コンストラクターをオーバーライドするコンストラクターで使用できますか。

いいえ、それは意味がありません。コンストラクタとセッタは2つの異なる方法であり、一方を他方で上書きすることはできません。
しかし、Scannerインスタンスを使用してユーザー入力を行うことで、コンストラクタからセッタを呼び出すことはできますが、実際のセッタのアプローチと同様に、コンストラクタに多すぎる責任を負わせるため、扱いにくい方法です。

これは、このようなコンストラクタを作成する最善の方法ですか?すべてのフィールドを移入し、コンストラクタを使用して

、それは次のようになります。あなたのオブジェクトは、それが作成されると不変になるように設計されている場合

Employee emp = new Employe(id, name, roles) 

は理にかなっています。

実際のケースでは、オブジェクトがコンストラクタまたはセッタを使用して不変であるように設計されていない場合でも、いずれの場合でもセッタを提供する必要があります。


ですから、責任(ユーザー入力を取得し、オブジェクトの状態を設定)を分離し、Employeeのインスタンス上の要件に応じてセッターまたはコンストラクタのいずれかの方法を使用する必要があり、あなたの質問に答えるために:

Employee emp = new Employe(id, name, roles) 

または

Employee emp = new Employe(); 
emp.setId(...); 
emp.setName(...); 
emp.setRoles(...); 
+1

優れた回答もあります。 1 + –

+0

Aha。別の優れた答え。説明のために多くのありがとう。 –

+1

私は同意します。週末に誰かが精神を守ることは良いことです。 – GhostCat

2

の2つのがあり良い答えはすでにあるが、私はあなたの問題へのもう一つの解決策を提供したいです。 @davidxxは既にあなたのオブジェクトが不変でなければならないと言っているので、すべての引数コンストラクタはsetterではなくaproachですが、より多くのフィールドがある場合を考えてみましょう。たとえば、従業員に給与、経験などがあります。コンストラクタは次のようになります。

Employee employee = new Employee(id, name, roles, salary, experience, ...); 

コンストラクタが長くなりすぎることがわかります。これはテレスコープ・コンストラクターと呼ばれます。従業員が2〜3の必須フィールドを持っていて、他のフィールドが必須ではないケースを考えてみましょう。このオブジェクトを作成するには、このようなコードを記述する必要があります:関数にnullを渡すあなたの頭痛の多くを引き起こしたこと

  1. :ので

    Employee employee = new Employee(id, name, roles, null, null, 0, ...); 
    

    これには問題があります。

  2. このコードは読みにくいです。

あなたが必要なフィールドのみをrecievesコンストラクタを追加することができますが、あなたはパラメータの異なる組み合わせを渡す必要があるとき、あなたは新しいコンストラクタ(速報オープンクローズ原則)たびに追加する必要があります。

public class Employee { 
    private int id; 
    private String name; 
    private List<Role> roles; 

    private Employee() { 
     roles = new ArrayList<>(); 
    } 

    public int getId() { 
     return id; 
    } 

    public String getName() { 
     return name; 
    } 

    public List<Role> getRoles() { 
     return roles; 
    } 

    public static class EmployeeBuilder { 

     private Employee employee; 

     public EmployeeBuilder() { 
      employee = new Employee(); 
     } 

     public EmployeeBuilder withId(Integer id) { 
      employee.id = id; 
      return this; 
     } 

     public EmployeeBuilder withName(String name) { 
      employee.name = name; 
      return this; 
     } 

     public EmployeeBuilder withRole(Role role) { 
      employee.roles.add(role); 
      return this; 
     } 

     public Employee build() { 
      return employee; 
     } 

    } 
} 

そして、あなたはこのようなあなたのオブジェクトを作成することができます:あなたのセッターは `Scanner`を使用すべきではない

Employee employee = new Employee.EmployeeBuilder() 
        .withId(1) 
        .withName("John") 
        .withRole(role1) 
        .withRole(role2) 
        .build(); 
+1

静的なビルダーパターン、素敵です。 1+ –

+1

@Petarペトロフ:私の質問(複数可)への解決策を作成する際に、別の設計側面をキャプチャするための多くのおかげで。私は別のコメントで言ったように、(大きな夢、大きなプロジェクトを構築するために非常に熱心で)いくつかの初心者は、自分のアプリ/ソリューションを設計する方法を考え出すで、このような劇的な意思決定に直面しています。 彼らは、このような設計上の問題を議論し、「Javaで大規模なプロジェクトを設計する方法」についての言及がある場合、私は疑問に思って。しかし、再び多くのありがとう。 –

関連する問題