13

そのため、Google と stackoverflow で検索しても、それ以上の結果が返されないことに驚いています。

OO プログラミング (私は Java を使用しています) で、1 対多の関係を正しく実装するにはどうすればよいですか?

クラスCustomerとクラスがありJobます。私のアプリケーションは、顧客のために仕事を完了する架空の会社です。私の現在の実装では、Jobクラスがクラスとは何の関係もありません。Customerクラスへの参照はまったくありません。このCustomerクラスは、コレクションとメソッドを使用して、顧客によって割り当てられた、または顧客のために完了したジョブに関する情報を保持、取得、および変更します。

Job問題は、特定のことがどの顧客に対して行われたかを知りたい場合はどうすればよいかということです。関連するこの記事のみを見つけました: http://www.ibm.com/developerworks/webservices/library/ws-tip-objrel3/index.html

作成者の実装によれば、JobコンストラクターにCustomerパラメーターを取得させ、それを格納して取得できるようにします。ただし、このモデルが一貫しているという保証はまったくありません。ジョブの関連顧客をジョブの対象外の顧客として設定したり、他の誰かのために行われた顧客にジョブを追加したりする制限はありません。これに関する助けをいただければ幸いです。

4

8 に答える 8

13

完全性を維持するための 100% 確実な方法はありません。

通常とられるアプローチは、1 つの方法を使用して関係を構築し、同じ方法で他の方向を構築することです。しかし、あなたが言うように、これは誰もそれを台無しにすることを妨げません.

次のステップは、いくつかのメソッドをパッケージからアクセスできるようにすることです。これにより、少なくとも、あなたのコードとは関係のないコードがそれを壊すことはありません:

class Parent {

  private Collection<Child> children;

  //note the default accessibility modifiers
  void addChild(Child) {
    children.add(child);
  }

  void removeChild(Child) {
    children.remove(child);
  }
}

class Child {

   private Parent parent;
   public void setParent(Parent parent){
     if (this.parent != null)
       this.parent.removeChild(this);
     this.parent = parent;
     this.parent.addChild(this);
   }
}

実際には、クラスでこの関係をモデル化することはあまりありません。代わりに、ある種のリポジトリで親のすべての子を検索します。

于 2012-04-10T11:02:48.583 に答える
5

複雑な (そしてゼロコードの) 答えを期待していなかったかもしれませんが、意図したとおりに防爆 API を構築するための解決策はありません。それは、パラダイム (OO) やプラットフォーム (Java) が原因ではなく、間違った分析を行ったことが原因です。トランザクションの世界 (実生活の問題をモデル化し、時間の経過とともに進化するすべてのシステムはトランザクションです) このコードは、ある時点で壊れることがあります。

// create
Job j1 = ...
Job j2 = ...
...
// modify
j1.doThis();
...

// access
j2.setSomeProperty(j1.someProperty);

当時j1.somePropertyはアクセスされてj1おり、j2存在すらできなかったからです:)

TL;DR

これに対する長い答えは不変性であり、ライフサイクルトランザクションの概念も導入します。他のすべての回答はその方法を教えてくれますが、代わりに理由を概説したいと思います。1 対多の関係には 2 つの側面があります

  1. 多くを持っています
  2. 属する

ACustomerが Jobを持っている場合B、 JobBが Customer に属している限り、システムは一貫していAます。これはさまざまな方法で実装できますが、これはトランザクション、つまり単純なアクションで構成される複雑なアクションで発生する必要があり、システムはトランザクションの実行が完了するまで利用できません。これは抽象的すぎて、あなたの質問とは無関係に思えますか? いいえ、そうではありません:)トランザクションシステムは、これらすべてのオブジェクトが有効な状態にある場合にのみ、したがってシステム全体が一貫している場合にのみ、クライアントがシステムのオブジェクトにアクセスできることを保証します。他の回答から、いくつかの問題を解決するために必要な処理量がわかるため、保証にはコストがかかります:パフォーマンス. これは、Java (およびその他の汎用 OO 言語) がそのままでは問題を解決できない理由を簡単に説明したものです。

もちろん、オブジェクト指向言語は、トランザクションの世界をモデル化することと、それにアクセスすることの両方に使用できますが、特別な注意を払う必要があり、いくつかの制約を課す必要があり、クライアント開発者には特別なプログラミング スタイルが必要です。通常、トランザクション システムは、 search (別名 query) とlockの 2 つのコマンドを提供します。クエリの結果は不変です。これは、撮影された特定の瞬間のシステムの写真 (つまりコピー) であり、写真を変更しても現実世界には明らかに影響しません。システムをどのように変更できますか?いつもの

  1. 必要に応じてシステム (またはその一部) をロックする
  2. オブジェクトを見つける:ローカルで読み書きできる実際のオブジェクトのコピー(写真) を返します
  3. ローカル コピーを変更する
  4. 変更されたオブジェクトをコミットします。つまり、システムが提供された入力に基づいてその状態を更新できるようにします
  5. (現在は役に立たない) ローカル オブジェクトへの参照を破棄します。システムが変更されたため、ローカル コピーは最新ではありません。

(ちなみに、ライフサイクルの概念がローカル オブジェクトとリモート オブジェクトにどのように適用されているかわかりますか?)

Sets、修飾子などを使用できfinalますが、トランザクションと不変性を導入するまでは、設計に欠陥があります。通常、Java アプリケーションはトランザクション機能を提供するデータベースに支えられており、多くの場合、DB は ORM (Hibernate など) と結合されてオブジェクト指向コードを記述します。

于 2012-04-10T16:28:40.983 に答える
2

他のデータ構造を使用する代わりにHashSetのようなSet実装を使用することで、重複がないことを確認できます。Jobを顧客に追加する代わりに、プライベート コンストラクターを持つ Job クラスに最終的な内部クラスを作成します。これにより、ラッパーの内部クラスはジョブ オブジェクトによってのみ作成できるようになります。Job コンストラクターにjobIDと customer をパラメーターとして取り込ませます。一貫性を維持するため - 顧客が Null の場合、ダミー ジョブを作成しないため、例外をスローします。

Customer のaddメソッドで、 JobUnit でラップされたJobが自身の ID と同じ顧客 ID を持っているかどうかを確認し、そうでない場合は Exception をスローします

Job クラスの顧客を置き換える場合、Customer クラスによって提供されるメソッドを使用してJobUnitを削除し、それ自体を新しい顧客に追加して、顧客参照を新しく渡された顧客に変更します。そうすれば、コードをより適切に推論できます。

顧客クラスは次のようになります。

public class Customer {

    Set<JobUnit> jobs=new HashSet<JobUnit>();    
    private Long id;
    public Customer(Long id){        
        this.id = id;
    }

    public boolean add(JobUnit unit) throws Exception{
       if(!unit.get().getCustomer().id.equals(id))
           throw new Exception(" cannot assign job to this customer");
        return jobs.add(unit);
    }

     public boolean remove(JobUnit unit){
        return jobs.remove(unit);
    }

    public Long getId() {
        return id;
    }

}

そしてジョブクラス:

public class Job {
Customer customer;
private Long id;

最終的な JobUnit 単位。

public Job(Long id,Customer customer) throws Exception{
    if(customer==null)
        throw new Exception("Customer cannot be null");
    this.customer = customer; 
   unit= new JobUnit(this);       
    this.customer.add(unit);
}

public void replace(Customer c) throws Exception{      
    this.customer.remove(unit);
    c.add(unit);
    this.customer=c;
}

public Customer getCustomer(){
    return customer;
}

/**
 * @return the id
 */
public Long getId() {
    return id;
}

public final class JobUnit{
    private final Job j;


    private JobUnit(Job j){
        this.j = j;

    }
    public Job get(){
        return j;
    }
 }
}

しかし、私が興味を持っていることの 1 つは、なぜ顧客オブジェクトにジョブを追加する必要があるのか​​ということです。どの顧客がどのジョブに割り当てられているかを確認するだけの場合は、ジョブを検査するだけでその情報が得られます。通常、やむを得ない場合を除き、循環参照を作成しないようにしています。また、ジョブが作成された後に顧客を置き換える必要がない場合は、Jobクラスの顧客フィールドを Final にし、メソッドを削除して設定または置き換えます。

ジョブに顧客を割り当てるための制限はデータベースで維持する必要があり、データベース エントリはチェック ポイントとして使用する必要があります。他の誰かのために行われた顧客へのジョブの追加に関しては、ジョブの顧客参照をチェックして、ジョブが追加されている顧客が保持している顧客と同じであることを確認するか、またはより良い方法で、単に参照を削除することができます。仕事の顧客とそれはあなたのために物事を簡素化します

于 2012-04-10T14:16:52.543 に答える
1

Customer オブジェクトが関係を所有している場合は、次の方法で行うことができます。

Job job = new Job();
job.setStuff(...);
customer.addJob(Job job) {
    this.jobs.add(job);
    job.setCustomer(this); //set/overwrite the customer for this job
}

//in the job class
public void setCustomer(Customer c) {
    if (this.customer==null) {
        this.customer = c;
    } // for the else{} you could throw a runtime exception
}

...所有権が逆の場合は、顧客を仕事に置き換えてください。

アイデアは、関係の所有者に一貫性を維持させることです。双方向の関係は、通常、一貫性管理が両方のエンティティに存在することを意味します。

于 2012-04-10T10:09:23.523 に答える
0

一貫性を維持する適切なセッター関数を作成します。たとえば、ジョブを作成するときはいつでも、コンストラクターで顧客を提供します。次に、ジョブコンストラクターは、顧客のジョブのリストに自分自身を追加します。または、顧客にジョブを追加するたびに、追加機能は、ジョブの顧客が追加先の顧客であることを確認する必要があります。または、これとあなたのニーズに合ったものと同様のもののいくつかの組み合わせ。

于 2012-04-10T10:06:42.470 に答える
-1

他のオブジェクトを持つオブジェクトにある種のコレクションを実装するだけです。たとえば、顧客では次のように言えます。

private List<Job> jobs; 

次に、ゲッターとセッターを使用して、値ジョブをこのリストに追加できます。これは基本的な OO のものです。インターネットで十分に検索したとは思いません。これらのテーマに関する情報はたくさんあります。

ところで、あらゆる種類のコレクション(セット、リスト、マップ)を使用できます

于 2012-04-10T10:27:51.543 に答える
-1

これが遅れていることはわかっていますが、これに対する別の方法は、問題を少し違った方法で見ることだと思います. 顧客は、顧客によって割り当てられた、または顧客のために完了したすべてのジョブのコレクションを保持するため、ジョブ クラスは、顧客によって完了されたすべてのジョブを持つという追加情報を持つ顧客のサブクラスであると考えることができます。次に、メイン クラスで顧客 ID を維持するだけでよく、それが継承されます。この設計により、各ジョブを顧客にリンクできるようになります。また、顧客のために、存在する仕事の数を知りたい場合も、それも得られます。

于 2016-10-18T20:48:55.937 に答える
-1

これが非常に遅れていることは申し訳ありませんが、継承モデルに従うことが最善の解決策であると感じる同様の問題に遭遇しました。仕事は、特定の顧客によって行われた/割り当てられた仕事であると考えてください。したがって、その場合、顧客なしではジョブが存在できないため、顧客はサブクラスであるジョブ(顧客のジョブを呼び出すことができます)を持つスーパークラスになります。顧客は、主にデータの取得を容易にするために、ジョブのリストも持っています。Job と Customer done には何らかの関係があるように見えるので、これは直感的には意味がありませんが、Job は顧客なしでは存在できないことを理解すると、それは単に顧客の延長になります。

于 2016-11-08T10:00:48.973 に答える