Вопрос по java, oop – Программирование отношений один ко многим

11

Поэтому я удивлен, что поиск в google и stackoverflow не дает больше результатов.

В ОО-программировании (я использую Java), как правильно реализовать отношение один ко многим?

У меня есть классCustomer и классJob, Мое заявление для фиктивной компании, которая выполняет работу для клиентов. Моя текущая реализация такова, чтоJob класс не имеет ничего общего сCustomer класс, там вообще нет ссылки на него.Customer Класс использует коллекцию и методы для хранения, извлечения и изменения информации о Заданиях, которые были назначены и / или завершены для клиента.

Вопрос в том, что если я хочу выяснить, для какого клиентаJob было сделано? Я нашел только эту статью, которая имеет отношение к делу:http://www.ibm.com/developerworks/webservices/library/ws-tip-objrel3/index.html.

Согласно реализации автора, я бы позволилJob конструктор взятьCustomer параметр, и сохраните его, чтобы я мог получить его. Тем не менее, я не вижу никакой гарантии, что эта модель может бытьconsistent, Не существует ограничений для назначения соответствующего клиента для работы в качестве клиента, для которого работа не была предназначена, и для добавления заданий клиентам, которые были выполнены для кого-то другого. Любая помощь в этом будет оценена.

Ваш Ответ

8   ответов
4

there is no solution to build your bombproof API the way you intend it, И это не потому, что парадигма (OO) или платформа (Java), а только потому, что вы сделали неправильный анализ. В транзакционном мире (каждая система, которая моделирует реальные проблемы жизни и их эволюцию во времениis transactional) Этот кодwill ever break в какой-то момент:

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

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

потому что в то времяj1.someProperty доступ,j1 а такжеj2 не мог даже существовать :)

TL;DR

Длинный ответ на этоimmutabilityи это также вводит понятияlife cycle а такжеtransactions, Все остальные ответы говорят вамhow to do это, вместо этого я хочу изложитьwhy, Отношение один ко многим имеет две стороны

has many belongs to

Ваша система работает до тех пор, пока клиентA имеет работуB, работаB принадлежит ЗаказчикуA, Вы можете реализовать это несколькими способами, но это должно произойтиtransactionсложное действие, состоящее из простых, и система должна бытьunavailble пока транзакция не закончила выполнение. Это кажется слишком абстрактным и не связанным с вашим вопросом? Нет, это не :) Транзакционная система гарантирует, что клиенты могут получить доступ к системным объектам, только если все эти объекты находятся вvalid stateследовательно, только если вся система является последовательной. Из других ответов вы видите объем обработки, необходимый для решенияsome проблемы, так что гарантия стоит денег:performance, Это простое объяснение того, почему Java (и другие OO-языки общего назначения) не могут решить вашу проблемуout of the box.

Конечно, язык ОО может быть использован для моделированияtransactional world и доступ к нему, но необходимо соблюдать особую осторожность, должны быть наложены некоторые ограничения и особый стиль программирования требуется для клиентов-разработчиков. Обычно транзакционная система предлагает две команды:search (иначе запрос) иlock, Результат запросаimmutableЭто фотография (то есть копия) системы в тот самый момент, когда она была сделана, и изменение фотографии, очевидно, не влияет на реальный мир. Как можно изменить систему? Обычно

lock the system (or parts of it) if/when needed locate an object: returns a copy (a photo) of the real object which can be read and written locally modify the local copy commit the modified object, ie let the system update its state based on provided input discard any reference to (now useless) local objects: the system has changed changed, so the local copy isn't up to date.

(BTW, can you see how the concept of life cycle is applied to local and remote objects?)

Вы можете пойти сSets,final модификаторы и так далее, но пока вы не введете транзакции и неизменность, у вашего дизайна будет недостаток. Обычно приложения Java поддерживаются базой данных, которая обеспечивает транзакционные функциональные возможности, и часто БД соединяется с ORM (например, Hibernate) для написания объектно-ориентированного кода.

Error: User Rate Limit Exceededwill have its limitsError: User Rate Limit Exceededeven the simplest one can be the best oneError: User Rate Limit Exceeded
Error: User Rate Limit Exceeded MarioDS
2

используяSet реализация какHashSet вместо использования другой структуры данных. И вместо добавленияJob для клиента создайте последний внутренний класс в классе Job, который имеет приватный конструктор. Это гарантирует, что внутренний класс оболочки может быть создан только объектом задания. Заставь тебя устроиться на работу в конструкторjobID и клиент в качестве параметра. Для обеспечения согласованности, если клиент имеет значение Null throw Exception, поскольку фиктивные задания не должны создаваться.

Вadd метод клиента, проверьте, еслиJob завернутыйJobUnit имеет тот же идентификатор клиента, что и собственный идентификатор, если не выдает исключение.

При замене клиента в классе Job удалитеJobUnit используя метод, предоставленный классом Customer, добавьте себя к новому клиенту и измените ссылку на нового пропущенного клиента. Таким образом, вы можете лучше рассуждать со своим кодом.

Вот как может выглядеть ваш класс клиентов.

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;
    }
 }
}

Но одна вещь, которая меня интересует, заключается в том, почему вам даже нужно добавлять рабочие места в объект заказчика? Если все, что вы хотите проверить, это увидеть, какой клиент был назначен на какую работу, простая проверка работы даст вам эту информацию. Как правило, я стараюсь не создавать круговые ссылки, если это неизбежно. Кроме того, если нет необходимости заменять клиента из задания после его создания, просто сделайте поле клиента Окончательным в полеJob Класс и удалить метод дляset или жеreplace Это.

Ограничение для назначения клиента на работу должно быть сохранено в базе данных, и запись в базе данных должна использоваться в качестве контрольной точки. Что касается добавления к клиенту заданий, которые были выполнены для кого-то другого, вы можете либо проверить ссылку на клиента в задании, чтобы убедиться, что клиент, к которому добавляется задание, является тем же, которого он держит, или даже лучше.simply remove any reference in customer for Job and it will simplify things for you.

1

вы можете сделать это следующим образом:

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
}

... если право собственности - наоборот, просто замените клиента на работу.

Идея состоит в том, чтобы владелец отношений поддерживал последовательность. Двунаправленные отношения обычно подразумевают, что управление согласованностью находится в обоих объектах.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded MarioDS
0

которая поддерживает последовательность. Например, всякий раз, когда вы создаете задание, вы указываете клиента в конструкторе. Конструктор заданий затем добавляет себя в список заданий клиента. Или всякий раз, когда вы добавляете задание к клиенту, функция добавления должна проверять, является ли заказчик заданием, к которому он добавляется. Или какое-то сочетание этого и подобных вещей с тем, что соответствует вашим потребностям.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededno oneError: User Rate Limit Exceeded MarioDS
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded MarioDS
Error: User Rate Limit Exceededcustomer.addJob(new Job(this, a, b, c));Error: User Rate Limit Exceeded MarioDS
9

Подход, который обычно используется, состоит в том, чтобы использовать один метод для построения отношений и создать другое направление в этом же методе. Но, как вы говорите, это никому не мешает возиться с этим.

Следующим шагом было бы сделать некоторые методы доступными для пакета, чтобы, по крайней мере, код, который не имеет ничего общего с вашим, не смог его сломать:

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);
   }
}

В действительности вы не будете часто моделировать эти отношения в своих классах. Вместо этого вы будете искать всех детей для родителя в каком-то хранилище.

@ powder366 хороший улов. Исправленный.
Спасибо, что дали очень хороший ответ на вопрос. Я принимаю, но оставлю вопрос открытым немного дольше, чтобы могло появиться больше ответов. MarioDS
@JoeriHendrickx Как бы выглядели объекты в хранилище? Я полагаю, что это будет какParentChild{Parent p, Collection<Child> c} а такжеRepository{ Collection<ParentChild> pc; returnParents(Child c){...}; returnChildren(Parent p){...} }.
В Parent нет опечатки, у нее есть два метода addChild ()
Error: User Rate Limit Exceeded
-1

что уже поздно, но я думаю, что другим способом было бы взглянуть на проблему немного по-другому. Поскольку клиент хранит коллекцию всех заданий, назначенных или выполненных для клиента, вы можете рассматривать класс заданий как подкласс клиента с дополнительной информацией о том, что все задания выполнены заказчиком. Тогда вам нужно будет только сохранить идентификатор клиента в основном классе, и он будет унаследован. Этот дизайн будет гарантировать, что каждая работа может быть связана с клиентом. Также, если для клиента вы хотите узнать, сколько рабочих мест присутствует, это тоже будет получено.

-1

я знаю, что это очень поздно, но я столкнулся с подобной проблемой, где я чувствую, что лучшее решение - это следовать модели наследования. Думайте о работе, как о работе, выполненной / подтвержденной конкретным клиентом. Таким образом, в этом случае Заказчик будет суперклассом, а задание (пусть это будет задание заказчика) является подклассом, поскольку задание не может существовать без заказчика. У клиента также будет список заданий, в первую очередь для облегчения извлечения данных. Интуитивно это не имеет смысла, поскольку «Работа и клиент», похоже, имеют какое-то отношение, однако, как только вы видите, что «Работа» не может существовать без клиента, она просто становится продолжением клиента.

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit ExceededtoString() на что это будет отображаться? Это нарушение принципа единоличной ответственности. В любом случае вы полностью игнорировали коллекцию, которая является источником потенциального несоответствия.
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
-1

который имеет другие объекты. Например, в клиенте вы могли бы сказать:

private List<Job> jobs; 

затем с помощью методов получения и установки вы можете добавлять значения заданий в этот список. Это базовые вещи ОО, я не думаю, что вы искали достаточно в Интернете. по этим предметам доступно много информации.

Кстати, вы можете использовать все виды коллекций (наборы, списки, карты)

Error: User Rate Limit Exceeded MarioDS
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded MarioDS

Похожие вопросы