Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/email/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在OO模型中添加双向关系的最佳实践_Java_Language Agnostic_Oop_Model_Db4o - Fatal编程技术网

Java 在OO模型中添加双向关系的最佳实践

Java 在OO模型中添加双向关系的最佳实践,java,language-agnostic,oop,model,db4o,Java,Language Agnostic,Oop,Model,Db4o,我正在努力想出一种在OO模型中添加双向关系的好方法。假设有一个客户可以下很多订单,也就是说,客户和订单类之间存在一对多的关联,需要在两个方向上进行遍历:对于特定客户,应该可以告诉他们下的所有订单,对于订单,应该可以告诉客户 下面是一段Java代码,尽管问题主要与语言无关: class Customer { private Set orders = new HashSet<Order> (); public void placeOrder (Order o) {

我正在努力想出一种在OO模型中添加双向关系的好方法。假设有一个客户可以下很多订单,也就是说,客户和订单类之间存在一对多的关联,需要在两个方向上进行遍历:对于特定客户,应该可以告诉他们下的所有订单,对于订单,应该可以告诉客户

下面是一段Java代码,尽管问题主要与语言无关:

class Customer {
 private Set orders = new HashSet<Order> ();

        public void placeOrder (Order o) {
     orders.add(o);
            o.setCustomer(this);
 }
}

class Order {
 private Customer customer;
        public void setCustomer (Customer c) {
  customer = c;
 }
}
而不是正确的

c.placeOrder(o);
形成单向链路而不是双向链路

在学习OOP的过程中,是否有人能帮助我们找到一种惯用的、实用的方法来解决这个问题,而不必求助于“反射”或奇特的框架(无论如何,它都依赖于反射)

还有一个类似的问题:,但是我觉得它没有回答我的请求


附言:非常感谢您提供在db4o之上实现业务模型的实际项目的源代码链接

首先,除非您计划在客户之间移动订单,否则我认为您不应该提供
setCustomer()
方法,客户应该是构造函数的参数,并且保持不变


然后,用户不应该访问构造函数,只使用工厂方法
Owner

没有单一答案。这实际上取决于所涉及的课程。在您的情况下,您显然不想让人们选择做一些无效的事情,因此我会放弃Order.SetCustomer


不过,情况并非总是如此。正如我所说,这取决于所涉及的类。

如果您在
Customer.placeOrder(Order)
中维护双向关系,为什么不在
Order.setCustomer(Customer)
中执行相同的操作呢


这看起来像是复制代码,但它解决了问题。不过,最简单的方法是尽可能避免双向关系。

这是一个非常有趣的问题,对OOP的理论和实践有着深远的影响。首先,我将告诉你快速和肮脏的方式(几乎)完成你的要求。一般来说,我不推荐这个解决方案,但由于没有人提到它(如果内存没有让我失望的话),Martin Fowler(UML蒸馏)的一本书中提到了它,所以它可能值得一谈;您可以从以下位置更改setCustomer方法的定义:

public void setCustomer (Customer c) {
    customer = c;
}
致:

并确保客户订单在同一包装中。如果不指定访问修饰符,setCustomer默认为package可见性,这意味着只能从同一个包中的类访问它。显然,这并不能保护您免受来自同一软件包中Customer以外的类的非法访问。此外,如果您决定将客户订单分为两个不同的包,则代码将中断

在Java的常见编程实践中,包可视性在很大程度上是可以容忍的;我感觉在C++社区内,<强>朋友>强>修改器不能像java中的包可见性那样容忍,尽管它有类似的用途。我真的不明白为什么,因为friend更具选择性:基本上,对于每个类,您可以指定其他能够访问第一个类的私有成员的friend类和函数

然而,毫无疑问,无论是Java的包可见性还是C++的friend都不能很好地代表OOP的含义,甚至不能代表基于对象的编程的含义(OOP基本上是OBP加上继承和多态性;从现在起我将使用OOP这个术语)。OOP的核心方面是存在称为对象的实体,它们通过相互发送消息进行通信。对象具有内部状态,但此状态只能由对象本身更改。状态通常是结构化的,即它基本上是名称、年龄和顺序等字段的集合。在大多数语言中,消息是同步的,不能像邮件或UDP数据包那样被错误地丢弃。当您编写c.placeOrder(o)时,这意味着发送方,即,正在向c发送消息。此消息的内容为placeOrdero

Department department = (Department)cbDepartment.getSelectedItem();
Person person = new Person(tfFirstName.getText(), tfLastName.getText());
Contract contract = new Contract(tfPositionName.getText(), Integer.parseInt(tfSalary.getText()));
department.hire(person, contract);
当对象收到消息时,它必须处理它。java,C++,c++和许多其他语言假定对象只能处理一个消息,如果它的类定义了一个方法,该方法具有适当的名称和形式参数列表。一个类的方法集称为其接口,Java和C#等语言也有一个适当的构造,即接口来建模一组方法的概念。消息c.placeOrder(o)的处理程序是以下方法:

public void placeOrder(Order o) {
    orders.add(o);
    o.setCustomer(this);
}
方法主体是编写指令的地方,这些指令将在必要时改变对象c的状态。在本例中,订单字段被修改

这本质上就是OOP的意思。OOP是在模拟环境中开发的,在模拟环境中,基本上有许多相互通信的黑匣子,每个黑匣子负责自己的内部状态

大多数现代语言都完全遵循此方案,但前提是您仅限于私有字段和公共/受保护方法。不过,也有一些陷阱。例如,在类Customer的方法中,您可以访问另一个Customer对象的私有字段,例如orders

你链接的页面上的两个答案其实都很好,
void setCustomer (Customer c) {
    customer = c;
}
public void placeOrder(Order o) {
    orders.add(o);
    o.setCustomer(this);
}
Department department = (Department)cbDepartment.getSelectedItem();
Person person = new Person(tfFirstName.getText(), tfLastName.getText());
Contract contract = new Contract(tfPositionName.getText(), Integer.parseInt(tfSalary.getText()));
department.hire(person, contract);
package com.example.payroll.domain;

public class Contract {

    private String mPositionName;
    private int mSalary;

    public Contract(String positionName, int salary) {
        mPositionName = positionName;
        mSalary = salary;
    }

    public String getPositionName() {
        return mPositionName;
    }

    public int getSalary() {
        return mSalary;
    }

    /*
        Not much business logic here. You can think
        about a contract as a very simple, immutable type,
        whose state doesn't change and that can't really
        answer to any message, like a piece of paper.
    */
}
package com.example.payroll.domain;

public class Person {

    private String mFirstName;
    private String mLastName;
    private Department mDepartment;
    private boolean mResigning;

    public Person(String firstName, String lastName) {
        mFirstName = firstName;
        mLastName = lastName;
        mDepartment = null;
        mResigning = false;
    }

    public String getFirstName() {
        return mFirstName;
    }

    public String getLastName() {
        return mLastName;
    }

    public Department getDepartment() {
        return mDepartment;
    }

    public boolean isResigning() {
        return mResigning;
    }

    // ========== Business logic ==========

    public void youAreHired(Department department) {
        assert(department != null);
        assert(mDepartment != department);
        assert(department.isBeingHired(this));

        if (mDepartment != null)
            resign();

        mDepartment = department;
    }

    public void youAreFired() {
        assert(mDepartment != null);
        assert(mDepartment.isBeingFired(this));

        mDepartment = null;
    }

    public void resign() {
        assert(mDepartment != null);

        mResigning = true;
        mDepartment.iResign(this);
        mDepartment = null;
        mResigning = false;
    }
}
package com.example.payroll.domain;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public class Department {

    private String mName;
    private Map<Person, Contract> mEmployees;
    private Person mBeingHired;
    private Person mBeingFired;

    public Department(String name) {
        mName = name;
        mEmployees = new HashMap<Person, Contract>();
        mBeingHired = null;
        mBeingFired = null;
    }

    public String getName() {
        return mName;
    }

    public Collection<Person> getEmployees() {
        return mEmployees.keySet();
    }

    public Contract getContract(Person employee) {
        return mEmployees.get(employee);
    }

    // ========== Business logic ==========

    public boolean isBeingHired(Person person) {
        return mBeingHired == person;
    }

    public boolean isBeingFired(Person person) {
        return mBeingFired == person;
    }

    public void hire(Person person, Contract contract) {
        assert(!mEmployees.containsKey(person));
        assert(!mEmployees.containsValue(contract));

        mBeingHired = person;
        mBeingHired.youAreHired(this);
        mEmployees.put(mBeingHired, contract);
        mBeingHired = null;
    }

    public void fire(Person person) {
        assert(mEmployees.containsKey(person));

        mBeingFired = person;
        mBeingFired.youAreFired();
        mEmployees.remove(mBeingFired);
        mBeingFired = null;
    }

    public void iResign(Person employee) {
        assert(mEmployees.containsKey(employee));
        assert(employee.isResigning());

        mEmployees.remove(employee);
    }
}
package com.example.payroll;

import com.example.payroll.domain.*;

public class App {

    private static Department resAndDev;
    private static Department production;
    private static Department[] departments;

    static {
        resAndDev = new Department("Research & Development");
        production = new Department("Production");
        departments = new Department[] {resAndDev, production};
    }

    public static void main(String[] args) {

        Person person = new Person("John", "Smith");

        printEmployees();
        resAndDev.hire(person, new Contract("Project Manager", 3270));
        printEmployees();
        production.hire(person, new Contract("Quality Control Analyst", 3680));
        printEmployees();
        production.fire(person);
        printEmployees();
    }

    private static void printEmployees() {

        for (Department department : departments) {
            System.out.println(String.format("Department: %s", department.getName()));

            for (Person employee : department.getEmployees()) {
                Contract contract = department.getContract(employee);

                System.out.println(String.format("  %s. %s, %s. Salary: EUR %d", contract.getPositionName(), employee.getFirstName(), employee.getLastName(), contract.getSalary()));
            }
        }

        System.out.println();
    }
}
    public void youAreHired(Department department) {
        assert(department != null);
        assert(mDepartment != department);
        assert(department.isBeingHired(this));

        if (mDepartment != null)
            resign();

        mDepartment = department;
    }
    public void youAreHired(Department department) {
        assert(department != null);
        assert(mDepartment == null);
        assert(department.isBeingHired(this));

        mDepartment = department;
    }
    public boolean youAreHired(Department department) {
        assert(department != null);
        assert(mDepartment != department);
        assert(department.isBeingHired(this));

        if (mDepartment != null)
            if (!resign())
                    return false;

        mDepartment = department;

        return true;
    }
class OrderManager {
    void placeOrder(Customer c, Order o){
        c.addOrder(o);
        o.setCustomer(c);
    }
}

class Customer {
    private Set<Order> orders = new LinkedHashSet<Order>();
    void addOrder(Order o){ orders.add(o); }
}

class Order {
    private Customer customer;
    void setCustomer(Customer c){ this.customer=c; }
}