11

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

Поэтому я удивлен, что поиск в google и stackoverflow не дает больше результатов. В ОО-программировании (я использую Java), как правильно реализовать отношение один ко многим? У меня есть классCustomer и классJob, Мое заявление для фиктивной ...

8 ответов

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
}

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

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

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.

0

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

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, Отношение один ко многим имеет две стороны

  1. has many
  2. belongs to

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

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

  1. lock the system (or parts of it) if/when needed
  2. locate an object: returns a copy (a photo) of the real object which can be read and written locally
  3. modify the local copy
  4. commit the modified object, ie let the system update its state based on provided input
  5. 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) для написания объектно-ориентированного кода.

-1

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

9

Нет 100% надежного способа сохранить целостность.

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

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

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

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

-1

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

-1

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

private List<Job> jobs; 

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

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

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