I have a list of car manufacturers, models, and trims. Each of those objects has different attributes on them. Ex. car makers can have import vs domestic, sport vs luxery, etc. My current setup looks something like below,
public Manufacturer {
private String manufacturerName; //Getter&Setter as well
private List<Model> modelList; //Getter&Setter as well
//additional attributes
}
public Model {
private String modelName;
private List<Trim> trimList; //Getter&Setter as well
//additional attributes
}
public Trim {
private String trimType; //Getter&Setter as well
//additional attributes
}
public ContainerClass {
public List<Manufacturer> manufacturerList;
}
Now I can create the objects as Mazda, 3, Grand Touring and associate the objects in the list. But if someone comes along and just wants a car that has a sunroof, it feels like a waste to have to dig into every possible manufacturer and every model for that manufacturer to see if the trim has a sunroof attribute. Are there any strategies I could use to make this easier for developers, including myself?
Note: I am at a point where I don't have a database because I don't have real data to fill the database, this is a stop gap until I get that data later, so please don't just say "create a database" :). Also I am loading this information from an ExamplesTable object of JBehave.
You can filter the manufacturers by some sub-streams:
container.getManufacturerList().stream()
.filter(manufacturer ->
manufacturer.getModelList().stream().anyMatch(model ->
model.getTrimList().stream().anyMatch(trim ->
trim.getTrimType().equals("sunroof"))))
.collect(Collectors.toList());
I have several arraylists which each contain player data for a specific team. Each object contains the following elements in order; Jersey Number, First Name, Last Name, Preferred Position, Goals, Assists. The user decides whether to view the data by goals or assists, and then the data is displayed in descending order. Goals and assists are both of int data type.
I will be able to display the data fine but what I am stuck on is how to sort the arrayList by one of these specific stats. Because the data from all the teams is in different arrayLists, and need to be sorted all together, do I need to combine the arrayLists into one master arrayList that will be sorted? As for the sorting, I have done a bit of research and it looks like I need to use a comparator? Could someone provide some assistance with this because I have never used these before and am quite lost. Examples would be great.
I have attached a few code snippets to hopefully provide some clarity.
ArrayList <blackTeam> blackTeam = new ArrayList <blackTeam>();
ArrayList <blueTeam> blueTeam = new ArrayList <blueTeam>();
ArrayList <greenTeam> greenTeam = new ArrayList <greenTeam>();
ArrayList <orangeTeam> orangeTeam = new ArrayList <orangeTeam>();
ArrayList <redTeam> redTeam = new ArrayList <redTeam>();
ArrayList <yellowTeam> yellowTeam = new ArrayList <yellowTeam>();
private void displaystatButtonActionPerformed(java.awt.event.ActionEvent evt) {
//sort arrayList by goals/assists
}
EDIT:
This is how my classes are set up, as well as how data is added to them. Hopefully this clears up some questions.
//add data to database
black = new blackTeam(jerseyNum, firstName, lastName, prefPosition, goals, assists);
blackTeam.add(black);
class blackTeam {
int goals, assists;
String jerseyNum, firstName, lastName, prefPosition;
blackTeam (String _jerseyNum, String _firstName, String _lastName, String _prefPosition, int _goals, int _assists) {
jerseyNum = _jerseyNum;
firstName = _firstName;
lastName = _lastName;
prefPosition = _prefPosition;
goals = _goals;
assists = _assists;
}
}
I have one these classes for each team.
I suggest using Comparator on your object, let me assume it is Team
public class Team{
private int jerseyNumber;
private String lastName;
...
public int getJerseyNumber(){
return jerseyNumber;
}
}
If you want to sort based on jersey number, generate JeseryNumberComaparator:
import java.util.Comparator;
public class JeseryNumberComaparator implements Comparator {
#Override
public int compare(Team t1, Team t2) {
// descending order (ascending order would be:
// t1.getJerseyNumber()-t2.getJerseyNumber())
return t1.getJerseyNumber()-t2.getJerseyNumber()
}
}
It will sort your list based on jersey number by:
Collections.sort(blackTeam, new JerseyNumberComparator());
For sorting Collection in Descending order (other than their natural sort order), you have to define your own Comparator.
For sorting on a specific field individually (one at a time), you have to define separate Comparator implementation.
In your class, you can define two individual Comparators. Here is example code.
static final Comparator<Team> SORT_TEAM_BY_GOALS_DESCENDING = new Comparator<Team>(){
public int compare(Team t1, Team t2){
return t2.getGoals() - t1.getGoals();
}
}
static final Comparator<Team> SORT_TEAM_BY_ASSIST_DESCENDING = new Comparator<Team>(){
public int compare(Team t1, Team t2){
return t2.getAssist() - t1.getAssist();
}
}
Make sure that, normal sort is always natural order, in your case for int it is always Ascending. In order to have Descending order, you need to do t2 - t1. t1 - t2 will give you natural Ascending order.
Now in order to use this Comparator, just use following code.
Collections.sort(team, SORT_TEAM_BY_GOALS_DESCENDING);
or
Collections.sort(team, SORT_TEAM_BY_ASSIST_DESCENDING);
And off course, if all these different color List (i.e. blackTeam and so on) are only for specific team identified by color, than add one more field to your Team class called 'color` which will identify each player along with what team they belongs to.
As long as your 6 classes blackTeam to yellowTeam all descend from the same parent, ie. that they are declared like this:
public class blackTeam extends Team { ... }
then you can make a new ArrayList<Team> and add them all to it:
ArrayList<Team> all = new ArrayList<>();
all.addAll(blackTeam);
all.addAll(blueTeam);
all.addAll(yellowTeam);
// etc...
Then you can sort this list using an instance of Comparator<Team>. Since Java8, however, there's a much neater way to create a comparator using lambda expressions:
all.sort((a, b) -> a.getScore() - b.getScore()); // or whatever attribute you want to compare on
If you want to do it the old fashioned way instead, then you can create an anonymous class like this:
all.sort(new Comparator<Team>() {
#Override
public int compare(Team a, Team b) {
return a.getScore() - b.getScore();
}
});
They amount to the same thing, but the lambda based approach is a bit less wordy!
Note that i suspect you don't actually want to have 6 different classes for the different colours. Are you sure you have understood the role of a class properly?
As per requested by my instructor, we were asked to write a Surgery Room booking program as an assignment. He gave us the following breakdown:
A hospital contains a list (LinkedList) of rooms.
Each room contains a name (I set mine as a room number int) and a list (LinkedList) of bookings.
Each list (LinkedList) of bookings contains a start and end time (I set mines as Date) and a team (another LinkedList).
I've set my GUI and I have an idea of how to construct the methods, I'm just finding it difficult to start since I cannot place two pieces of information in one node (like, putting a name and a list of bookings for a particular room).
If it helps, I have a fully implemented:
LinkedList<T> extends AbstractSequentialList<T> implements List<T>, Deque<T>, Cloneable, Serializable
He gave us most of the code, but although it is mostly complete I'm still a little clueless.
Does anyone have any insight on how to deal with the LinkedList issue? Thanks!
Create a POJO, a class with two fields, and make your LinkedList of that POJO type. For example,
public class Room {
private final int number;
private final List<Bookings> bookings = new LinkedList<>();
public Room(int number) {
this.number = number;
}
public int getNumber() {
return this.number;
}
public List<Bookings> getBookings() {
return bookings;
}
}
And then, you can have a
List<Room> rooms = new LinkedList<>();
Above, creating Bookings with start and end Dates left as an exercise for the reader.
I am trying to replace element in collection with new modified version. Below is short code that aims to demonstrate what I'd like to achieve.
The whole idea is that I have one object that consists of collections of other objects. At some point in time I am expecting that this objects in collections (in my example phones) might require some modifications and I'd like to modify the code in one place only.
I know that in order to update the object's attributes I can use setters while iterating through the collection as demonstrated below. But maybe there is better, more general way to achieve that.
public class Customer {
private int id;
private Collection<Phone> phoneCollection;
public Customer() {
phoneCollection = new ArrayList<>();
}
//getters and setters
}
and Phone class
public class Phone {
private int id;
private String number;
private String name;
//getters and setters
}
and
public static void main(String[] args) {
Customer c = new Customer();
c.addPhone(new Phone(1, "12345", "aaa"));
c.addPhone(new Phone(2, "34567", "bbb"));
System.out.println(c);
Phone p = new Phone(2, "9999999", "new name");
Collection<Phone> col = c.getPhoneCollection();
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
// This is working fine
// phone.setNumber(p.getNumber());
// phone.setName(p.getName());
// But I'd like to replace whole object if possible and this is not working, at least not that way
phone = p;
}
}
System.out.println(c);
}
}
Is this possible to achieve what I want?
I tried copy constructor idea and other methods I found searching the net but none of them was working like I would expect.
EDIT 1
After reading some comments I got an idea
I added the following method to my Phone class
public static void replace(Phone org, Phone dst){
org.setName(dst.getName());
org.setNumber(dst.getNumber());
}
and now my foreach part looks like that
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
Phone.replace(phone, p);
}
}
And it does the job.
Now if I change the Phone class attributes I only need to change that method. Do you think it is OK solving the issue that way?
You should not modify the collection while you're iterating through it; that's likely to earn you a ConcurrentModificationException. You can scan the collection for the first object that matches your search criterion. Then you can exit the loop, remove the old object, and add the new one.
Collection<Phone> col = c.getPhoneCollection();
Phone original = null;
for (Phone phone : col) {
if (phone.getId() == p.getId()) {
original = phone;
break;
}
}
if (original != null) {
Phone replacement = new Phone(original);
replacement.setNumber(p.getNumber());
replacement.setName(p.getName());
col.remove(original);
col.add(replacement);
}
Alternatively, you could declare a more specific type of collection, such as a List, that would allow you to work with indexes, which would make the replacement step much more efficient.
If your phone IDs are unique to each phone, you should consider using a Map<Integer, Phone> that maps each phone ID to the corresponding phone. (Alternatively, you could use some sort of third-party sparse array structure that doesn't involve boxing each ID into an Integer.) Of course, if your IDs aren't unique, then you might want to modify the above to gather a secondary collection of all matching phones (and reconsider the logic of your existing code as well).
You can also use a Set (HashSet), this is only when you don't want to do the way Mike suggested.
Use the Phone as an item in the set. Don't forget to implement hashCode() and equals() in Phone. hashCode() should return the id, as it is supposed to be unique.
Since you are concerned about replacing the item, here's how HashSet will help you :
Create an instance of your object.
Remove the object you want to replace from the set.
Add the new object (you created in step 1) back to the set.
Both these operations 2 & 3 are guaranteed in O(1) / constant time.
You don't need to maintain a map for this problem, that's redundant.
If you want to get the object from the collection itself and then modify it, then HashMap would be better, search is guaranteed in O(1) time.
Instead of a list, use a map with the Phone's id as the key. Then your code looks like this:
public static void main(String[] args) {
Customer c = new Customer();
c.addPhone(new Phone(1, "12345", "aaa"));
c.addPhone(new Phone(2, "34567", "bbb"));
System.out.println(c);
Phone p = new Phone(2, "9999999", "new name");
Map<Integer, Phone> phoneMap = c.getPhoneMap();
phoneMap.put(p.getId(), p);
System.out.println(c);
}
If you take the object out from the collection and update its properties, it will get reflected in the same object in collection too.. Hence, you dont have to technically replace object after updating it.
As "Mike M." pointed out, you can use hashmap to retrieve the object quickly without iteration and update the object values.
If order matters to you, you can change Collection to List (Since you're always using an ArrayList anyway) and then:
int index = col.indexOf(phone);
col.remove(phone);
col.add(p, index);
I'm struggling to come up with a good way of adding a bidirectional relation in OO model. Let's say there is a Customer who can place many Orders, that is to say there is a one-to-many association between Customer and Order classes that need to be traversable in both directions: for a particular customer it should be possible to tell all orders they have placed, for an order it should be possible to tell the customer.
Here is a snippet of Java code, although the question is largely language-agnostic:
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;
}
}
What buggers me is that given the model someone could easily call:
o.setCustomer(c);
instead of correct
c.placeOrder(o);
forming unidirectional link instead of bidirectional one.
Still learning OOP, could anyone please help with what would be an idiomatic and practical way of solving this problem without resorting to "reflection" or fancy frameworks (that would anyway rely on reflection).
P.S. There is a similar question: Managing bidirectional associations in my java model, however I don't feel it answers my plea.
P.S.S. Any links to source code of real-life projects implementing business model on top of db4o are greatly appreciated!
This is a very interesting question, which has profound implications on the theory and practice of OOP. First I will tell you the quick and dirty way to (almost) accomplish what you requested. In general I don't recommend this solution, but since nobody mentioned it and (if memory doesn't fail me) it is mentioned in a book from Martin Fowler (UML Distilled), it is probably worth talking about; you can change the definition of the setCustomer method from:
public void setCustomer (Customer c) {
customer = c;
}
to:
void setCustomer (Customer c) {
customer = c;
}
and make sure Customer and Order are in the same package. If you don't specify an access modifier, setCustomer defaults to package visibility, which means it will be only accessible from classes within the same package. Obviously this does not protect you from illegitimate access from classes other than Customer within the same package. Also, your code will break if you decide to move Customer and Order in two different packages.
Package visibility is largely tolerated in common programming practice in Java; I feel like within the C++ community the friend modifier is not as tolerated as package visibility in Java, despite the fact that it serves a similar purpose. I can't really understand why, because friend is much more selective: basically for each class you can specify other friend classes and functions which will be able to access the private members of the first class.
However, there are no doubts that neither Java's package visibility nor C++'s friend are good representatives of what OOP means, and not even of what Object-Based Programming means (OOP is basically OBP plus inheritance and polymorphism; I'll use the term OOP from now on). The core aspect of OOP is that there are entities called objects, and they communicate by sending messages to each other. Objects have an internal state, but this state can only be altered by the object itself. State is typically structured i.e. it is basically a collection of fields such as name, age and orders. In most languages messages are synchronous and they can't be dropped by mistake, like a mail or a UDP packet. When you write c.placeOrder(o) it means that sender, which is this, is sending a message to c. The contents of this message are placeOrder and o.
When an object receives a message it must handle it. Java, C++, C# and a lot of other languages assume that an object can handle a message only if its class defines a method with an appropriate name and list of formal parameters. The set of the methods of a class is called its interface, and languages such as Java and C# also have an appropriate construct, namely interface to model the concept of a set of methods. The handler for the message c.placeOrder(o) is the method:
public void placeOrder(Order o) {
orders.add(o);
o.setCustomer(this);
}
The body of the method is where you write the instructions that will alter the state of object c, if necessary. In this example the orders field is modified.
This is, in essence, what OOP means. OOP was developed in the context of simulations, in which you basically have a lot of black boxes that communicate with each other, and each box is responsible for its own internal state.
Most modern languages adhere perfectly to this scheme, but only if you restrict yourself to private fields and public/protected methods. There are a few gotchas, though. For instance, within a method of class Customer you could access the private fields, such as orders, of another Customer object.
The two answers on the page you linked are actually very good, and I upvoted both. However, I think, it is completely reasonable with respect to OOP, to have a real bidirectional association, as you described. The reason is that to send a message to someone, you must have a reference to him. That is why I'll try to outline what the problem is, and why we OOP programmers sometimes struggle with this. Long story short, real OOP is sometimes tedious, and very akin to a complex formal method. But it produces code that is easier to read, modify and extend, and in general saves you from a lot of headaches. I've been wanting to write this down for a while, and I think your question is a good excuse to do it.
The main problem with OOP techniques arises whenever a group of object must alter the internal state simultaneously, as a result of an external request, dictated by business logic. For instance, when a person is hired, lots of stuff happen. 1) The employee must be configured to point to his department; 2) he must be added to the list of hired employees in the department; 3) something else must be added somewhere else, like a copy of the contract (maybe even a scan of it), insurance information and so on. The first two actions that I cited are exactly an example of establishing (and maintaining, when the employee is fired or transferred) a bidirectional association, like the one you described between customers and orders.
In procedural programming Person, Department and Contract would be structures, and a global procedure like hirePersonInDepartmentWithContract associated to the click of a button in an user interface would manipulate 3 instances of these structures by the means of three pointers. The entire business logic is inside this function, and it must take into consideration every possible special case while updating the state of these three objects. For instance, there is the possibility that when you click the button to hire someone, he is already employed in another department, or even worse in the same. And computer scientists know that special cases are bad. Hiring a person is basically a very complex use case, with lots of extensions which don't happen very often, but that must be considered.
Real OOP mandates instead that objects must exchange messages to accomplish this task. The business logic is split among the responsibilities of several objects. CRC cards are an informal tool to study business logic in OOP.
To get from the valid state where John is unemployed, to the other valid state where he is a project manager at the R&D department, it is necessary to go through a number of invalid states, at least one. So there is an initial state, an invalid state and a final state, and at least two messages exchanged between a person and a department. You can also be sure that one message must be received by the department, to give it a chance of altering its internal state, and another one must be received by the person, for the same reason. The middle state is invalid in the sense that it doesn't really exist in the real world, or maybe exists but is of no importance. However, the logical model in your application must in a way keep track of it.
Basically the idea is that when the human resource guy fills the "New Employee" JFrame and clicks the "Hire" JButton, the selected department is retrieved from a JComboBox, which in turn may have been populated from a database, and a new Person is created based on the information inside the various JComponents. Maybe a job contract is created containing at least the name of the position and the salary. Finally there is appropriate business logic that wires all the objects together and triggers updates for all the states. This business logic is triggered by a method called hire defined in class Department, which takes as arguments a Person and a Contract. All of this may happen in the ActionListener of the JButton.
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);
I would like to stress what's going on at line 4, in OOP terms; this (which in our case is the ActionListener, is sending a message to department, saying they must hire person under contract. Let's have a look at a plausible implementation of these three classes.
Contract is a very simple class.
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.
*/
}
Person is way more interesting.
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;
}
}
Department is quite cool.
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);
}
}
The messages I defined have, at the very least, very pittoresque names; in a real application you might not want to use names like these, but in the context of this example they help to model the interactions between objects in a meaningful and intuitive way.
Department can receive the following messages:
isBeingHired: the sender wants to know whether a particular person is in the process of being hired by the department.
isBeingFired: the sender wants to know whether a particular person is in the process of being fired by the department.
hire: the sender wants the department to hire a person with a specified contract.
fire: the sender wants the department to fire an employee.
iResign: the sender is likely an employee, and is telling the department that he is resigning.
Person can receive the following messages:
youAreHired: the department sends this message to inform the person that he is hired.
youAreFired: the department sends this message to inform the employee that he is fired.
resign: the sender wants the person to resign from his current position. Note that an employee who was hired by another department can send the resign message to himself in order to quit the old job.
The fields Person.mResigning, Department.isBeingHired, Department.isBeingFired are what I use to encode the aforementioned invalid states: when either one of them is "non-zero", the application is in an invalid state, but is on its way to a valid one.
Also note that there are no set methods; this contrasts with the common practice of working with JavaBeans. JavaBeans are in essence very similar to C structures, because they tend to have a set/get (or set/is for boolean) pair for every private property. However they do allow for validation of set, for instance you can check that a String being passed to a set method is not-null and not empty and eventually raise an exception.
I wrote this little library in less than a hour. Then I wrote a driver program and it worked correctly with the JVM -ea switch (enable assertions) at the very first run.
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();
}
}
The fact that it worked is not the cool thing though; the cool thing is that only the hiring or firing department is authorized to send youAreHired and youAreFired messages to the person that is being hired or fired; in a similar way, only a resigning employee can send the iResign message to its department, and only to that department; any other illegitimate message sent from main would trigger an assertion. In a real program you would use exceptions instead of assertions.
Is all of this overkill? This example is admittedly a little extreme. But I feel like this is the essence of OOP. Objects must cooperate to achieve a certain goal i.e. changing the global state of the application according to predetermined pieces of business logic, in this case hiring, firing and resign. Some programmers think that business problems are not suited for OOP, but I disagree; business problems are basically workflows, and they are very simple tasks by themselves, but they involve a lot of actors (i.e. objects), which communicate through messages. Inheritance, polymorphism, and all the patterns are welcome extensions, but they are not the base of the object-oriented process. In particular, reference-based associations are often preferred to implementation inheritance.
Note that by using static analysis, design-by-contract and automatic theorem provers, you would be able to verify that your program is correct, for any possible input, without running it. OOP is the abstraction framework that enables you to think this way. It is not necessarily more compact than procedural programming, and it does not automatically lead to code reuse. But I insist that it is easier to read, modify and extend; let's have a look at this method:
public void youAreHired(Department department) {
assert(department != null);
assert(mDepartment != department);
assert(department.isBeingHired(this));
if (mDepartment != null)
resign();
mDepartment = department;
}
The business logic relevant to the use case is the assignment at the end; the if statement is an extension, a special case that only occurs when the person is already an employee in another department. The first three assertions describe forbidden special cases. If one day we want to forbid this automatic resign from the previous department we only need to modify this method:
public void youAreHired(Department department) {
assert(department != null);
assert(mDepartment == null);
assert(department.isBeingHired(this));
mDepartment = department;
}
We can also extend the application by making youAreHired a boolean function, which returns true only if the old department is ok with the new hiring. Obviously we may need to change something else, in my case I made Person.resign a boolean function, which in turn may require Department.iResign to be a boolean function:
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;
}
Now the current employeer has the final word in determining whether an employee can be transferred to another department. The current department could delegate the responsibility of determining this to a Strategy which may in turn take into consideration the projects in which the employee is involved, their deadlines and various contractual constraints.
In essence, adding an order to a customer really is part of business logic. If a bidirectional association is required, and reflection is not an option, and none of the solutions proposed on this and the linked question are satisfactory, I think the only solution is something like this.
first, unless you plan on moving orders between customers, I think you shouldn't provide a setCustomer() method, the customer should be a parameter for the constructor and leave it unchanged.
then, the constructor shouldn't be accessible for the user, only use the factory method of Owner.
There is no single answer. It really depends on the classes involved. In your case, you obviously don't want to give people the option of doing something invalid so I would get rid of Order.SetCustomer.
That may not always be the case though. Like I said, it depends on the classes involved.
If you are maintaining the bidirectional relationship in Customer.placeOrder(Order), why don't you do the same thing in Order.setCustomer(Customer)?
class Order {
private Customer customer;
public void setCustomer (Customer c) {
customer = c;
c.getOrders().add(this);
// ... or Customer.placeOrder(this)
}
}
It seems like duplicating code but it solves the problem. The simpler thing to do though is to avoid bidirectional relationships where possible.
I think the best way in this case is to delegate the responsibility for wiring to another class:
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; }
}