I don't know what are the best practices in my case :
1:
public class garage {
private List<Car> cars = new ArrayList<Cars>();
public String getCarSeatSomething(String carName, String brandName) {
for(Car car : cars){
if(car.getName().equals(carName)){
Seats seats = car.getSeats();
List<Brand> brands = seats.getBrands();
for(Brand brand: brands ){
if(brand.getName().equals(brandName)){
return brand.something();
}
}
}
}
return null;
}
...
}
I have many method like this, so I will have some redundant code with this solution.
Moreover, in my program, it's not "normal" that we don't find the car, so I think I have to use Exception no ?
2 :
public class Garage {
private List<Car> cars = new ArrayList<Car>();
public Something getCarSeatSomething(String carName, String brandName) {
Car car = searchCar(carName);
if(car == null)
return null;
else{
Seats seats = car.getSeats();
return seats.getSomething(brandName);
}
}
...
}
public class Seats {
private List<Brand> brands = new ArrayList<Brand>();
protected Something getSomething(brandName){
Brand brand = searchBrand(brandName);
if(brand == null)
return null;
else
return brand.something();
}
...
}
Less redundant code and less code for each method of the class Garage because the search are only in searchBrand and searchCar.
But I have allways the problem of exceptions.
So, my last solution, is to throw exception in the searchBrand and searchCar methods, add throws at all the methods (like getCarSeatSomething) which use searchBrand/searchCar and try ... catch when I use these methods (like getCarSeatSomething).
Is that correct ?
If not, have you got better ideas ?
It looks like car name and brand name will be user provided input. In that case, you should expect users to provide names that do not exist. It is not exceptional. Return null and in the top layer, return an error message to the user. It seems reasonable that you might try to "get" something and not get it at all. null is appropriate here. But make sure you are consistent about this behavior across your application and you document it.
If an entity must contain a particular attribute then getter methods for that attribute should throw an exception if the entity cannot provide the attribute.
If it is optional for an entity to contain the attribute, then getter methods should not throw an exception.
Some scenarios will involve both cases, where in some contexts access to the attribute is mandatory and in other contexts it is optional. Then you should provide two "getter" methods, one that will throw an exception if the attribute cannot be returned, and the other an Optional value. I recommend using the method naming convention of getEntity for mandatory getter methods and findEntity for optional getter methods.
In the case where an entity must contain a particular attribute and the getter method cannot complete the request, you have a malformed entity. If your entity is throwing exceptions because it cannot return mandatory attributes you have a bug or problems with how you created the entity.
Entities should never be created without mandatory attributes. Constructors and factories should enforce mandatory attributes. For entities that must be created and not fully formed (like Data Access Objects) separate validation should be applied to the entities before being used. Or separate your domain entity and DAO into separate but equivalent types.
To describe the situation: you have a elaborate data hierarchy, with possibly a chained access:
x.getLiat().get(3).getAs().lookupB("a1").getC();
This may lead to a design with either expectable NullPointerExceptions to be handled (ugly) or Excptions.
Java 8 proposes Optional<T> to explicitly handle in one expression what otherwise could be a null.
x.getA().orElse(a).getB().orElseThrow(() -> new XException()).getC();
Better yet is to use java 8's streams, with filtering, mapping, find any/first capabilities.
private List<Car> cars = new ArrayList<>();
public Optional<String> getCarSeatSomething(String carName, String brandName) {
return cars.stream()
.filter((car) -> car.getName().equals(carName))
.flatMap{(car) -> car.getSeats())
.flatMap((seats) -> seats.getBrands())
.filter((brand) -> brand.getName().equals(brandName))
.findFirst();
}
Related
I am using this logic and try to return new Employee() if no employee is found.
However, when request.getId() is null, it throws exception, and I am waiting that it executes orElse() part. However, it does not. So, should I use another optional method, e.g. orElseGet() or ifPresentOrElse()?
How can I fix this problem in a smart way?
Employee employee = employeeRepository.findById(request.getId())
.orElse(new Employee());
Update: If using Optional is not a good idea, I think of using the following approach:
Employee employee = new Employee();
if (id != null) {
employee = employeeRepository.findById(id)
.orElse(new Employee());
}
Any idea for that?
As mentioned in the comments, the Optional doesn't do anything in this case. To solve it you need to handle the null value by yourself. You could do this by making the request.getId() return an Optional by itself (since it can be null).
Now you can chain these Optional's using the flatMap() operator.
public class MyRequestClass {
private Long id;
// Change your getter to this, or make a new one, eg. 'getOptionalId()'
public Optional<Long> getId() {
return Optional.ofNullable(id);
}
}
Now you can refactor your code to:
Employee employee = request
.getId()
.flatMap(employeeRepository::findById) // Using flatMap() in stead of map()
.orElseGet(Employee::new); // I changed this part so that new Employee() is
// called lazily when no Employee was found in
// the database (or when the request has no ID)
Avoid using Optional to hide a Null-checks
Using an Optional to hide a null-check - is an Antipattern.
There's nothing wrong with implicit null-checks e.g. if (something == null) and more over Optional wasn't designed to perform null-checks.
Here's a quote from the answer by Stuart Marks, Java and OpenJDK developer:
The primary use of Optional is as follows: (slide 36)
Optional is intended to provide a limited mechanism for library method
return types where there is a clear need to represent "no result," and
where using null for that is overwhelmingly likely to cause errors.
The ability to chain methods from an Optional is undoubtedly very
cool, and in some cases it reduces the clutter from conditional logic.
But quite often this doesn't work out. A typical code smell is,
instead of the code using method chaining to handle an Optional
returned from some method, it creates an Optional from something
that's nullable, in order to chain methods and avoid conditionals.
(the emphasis is mine)
From the quote above, it's clear that Optional was introduced in the JDK to provide a limited mechanism for return types. Any other cases like using optional as a field, storing it into a Collection, creating an optional to substitute a null-check or/and in order to chain methods on it are considered to be an abuse of Optional.
Also have a look at another answer by Stuart Marks: Should Optional.ofNullable() be used for null check?
That said, the cleaner way of retrieving employee by id, when id in the request object can be null would be the following:
public Employee getEmployeeById(Request request) {
Long id = request.getId();
if (id == null) return new Employee();
return employeeRepository.findById(id)
.orElse(new Employee());
}
If fill uncomfortable that new Employee() appears twice in the code, then you can reimplement it like that:
public Employee getEmployeeById(Request request) {
Long id = request.getId();
Optional<Employee> result = Optional.empty();
if (id != null) result = employeeRepository.findById(id);
return id == null || result.isEmpty() ? new Employee() : result.get();
}
How about null-handling of the request.getId() and avoiding falling into the exception?
Use one of the following, assuming employeeRepository#findById returns Optional<Employee>:
Employee employee = Optional.ofNullable(request.getId())
.flatMap(id -> employeeRepository.findById(id))
.orElse(new Employee());
Employee employee = Optional.ofNullable(request.getId())
.flatMap(EmployeeRepository::findById)
.ofElse(new Employee());
I really like the addition of records in Java 14, at least as a preview feature, as it helps to reduce my need to use lombok for simple, immutable "data holders". But I'm having an issue with the implementation of nullable components. I'm trying to avoid returning null in my codebase to indicate that a value might not be present. Therefore I currently often use something like the following pattern with lombok.
#Value
public class MyClass {
String id;
#Nullable String value;
Optional<String> getValue() { // overwrite the generated getter
return Optional.ofNullable(this.value);
}
}
When I try the same pattern now with records, this is not allowed stating incorrect component accessor return type.
record MyRecord (String id, #Nullable String value){
Optional<String> value(){
return Optional.ofNullable(this.value);
}
}
Since I thought the usage of Optionals as return types is now preferred, I'm really wondering why this restriction is in place. Is my understanding of the usage wrong? How can I achieve the same, without adding another accessor with another signature which does not hide the default one? Should Optional not be used in this case at all?
A record comprises attributes that primarily define its state. The derivation of the accessors, constructors, etc. is completely based on this state of the records.
Now in your example, the state of the attribute value is null, hence the access using the default implementation ends up providing the true state. To provide customized access to this attribute you are instead looking for an overridden API that wraps the actual state and further provides an Optional return type.
Of course, as you mentioned one of the ways to deal with it would be to have a custom implementation included in the record definition itself
record MyClass(String id, String value) {
Optional<String> getValue() {
return Optional.ofNullable(value());
}
}
Alternatively, you could decouple the read and write APIs from the data carrier in a separate class and pass on the record instance to them for custom accesses.
The most relevant quote from JEP 384: Records that I found would be(formatting mine):
A record declares its state -- the group of variables -- and commits
to an API that matches that state. This means that records give up a
freedom that classes usually enjoy -- the ability to decouple a
class's API from its internal representation -- but in return, records
become significantly more concise.
Due to restrictions placed on records, namely that canonical constructor type needs to match accessor type, a pragmatic way to use Optional with records would be to define it as a property type:
record MyRecord (String id, Optional<String> value){
}
A point has been made that this is problematic due to the fact that null might be passed as a value to the constructor. This can be solved by forbidding such MyRecord invariants through canonical constructor:
record MyRecord(String id, Optional<String> value) {
MyRecord(String id, Optional<String> value) {
this.id = id;
this.value = Objects.requireNonNull(value);
}
}
In practice most common libraries or frameworks (e.g. Jackson, Spring) have support for recognizing Optional type and translating null into Optional.empty() automatically so whether this is an issue that needs to be tackled in your particular instance depends on context. I recommend researching support for Optional in your codebase before cluttering your code possibly unnecessary.
Credits go to Holger! I really like his proposed way of questioning the actual need of null. Thus with a short example, I wanted to give his approach a bit more space, even if a bit convoluted for this use-case.
interface ConversionResult<T> {
String raw();
default Optional<T> value(){
return Optional.empty();
}
default Optional<String> error(){
return Optional.empty();
}
default void ifOk(Consumer<T> okAction) {
value().ifPresent(okAction);
}
default void okOrError(Consumer<T> okAction, Consumer<String> errorAction){
value().ifPresent(okAction);
error().ifPresent(errorAction);
}
static ConversionResult<LocalDate> ofDate(String raw, String pattern){
try {
var value = LocalDate.parse(raw, DateTimeFormatter.ofPattern(pattern));
return new Ok<>(raw, value);
} catch (Exception e){
var error = String.format("Invalid date value '%s'. Expected pattern '%s'.", raw, pattern);
return new Error<>(raw, error);
}
}
// more conversion operations
}
record Ok<T>(String raw, T actualValue) implements ConversionResult<T> {
public Optional<T> value(){
return Optional.of(actualValue);
}
}
record Error<T>(String raw, String actualError) implements ConversionResult<T> {
public Optional<String> error(){
return Optional.of(actualError);
}
}
Usage would be something like
var okConv = ConversionResult.ofDate("12.03.2020", "dd.MM.yyyy");
okConv.okOrError(
v -> System.out.println("SUCCESS: "+v),
e -> System.err.println("FAILURE: "+e)
);
System.out.println(okConv);
System.out.println();
var failedConv = ConversionResult.ofDate("12.03.2020", "yyyy-MM-dd");
failedConv.okOrError(
v -> System.out.println("SUCCESS: "+v),
e -> System.err.println("FAILURE: "+e)
);
System.out.println(failedConv);
which leads to the following output...
SUCCESS: 2020-03-12
Ok[raw=12.03.2020, actualValue=2020-03-12]
FAILURE: Invalid date value '12.03.2020'. Expected pattern 'yyyy-MM-dd'.
Error[raw=12.03.2020, actualError=Invalid date value '12.03.2020'. Expected pattern 'yyyy-MM-dd'.]
The only minor issue is that the toString prints now the actual... variants. And of course we do not NEED to use records for this.
Don't have the rep to comment, but I just wanted to point out that you've essentially reinvented the Either datatype. https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Either.html or https://www.scala-lang.org/api/2.9.3/scala/Either.html. I find Try, Either, and Validation to be incredibly useful for parsing and there are a few java libraries with this functionality that I use: https://github.com/aol/cyclops/tree/master/cyclops and https://www.vavr.io/vavr-docs/#_either.
Unfortunately, I think your main question is still open (and I'd be interested in finding an answer).
doing something like
RecordA(String a)
RecordAandB(String a, Integer b)
to deal with an immutable data carrier with a null b seems bad, but wrapping recordA(String a, Integer b) to have an Optional getB somewhere else seems contra-productive. There's almost no point to the record class then and I think the lombok #Value is still the best answer. I'm just concerned that it won't play well with deconstruction for pattern matching.
I want to implement some simple endpoint in spring, trying to be as much Restful as possible and reduce the number of URL to use. Here are the GET url I want to call: (this is a simplified version)
GET /users
GET /users?id=123
GET /users?username=xyz
I used this controller:
#GetMapping()
public #ResponseBody
OutputUserDTO getUserByParameter(#RequestParam(required = false) String id,
#RequestParam(required = false) String username) {
if (id != null && !id.isEmpty()) {
return userService.getUserById(id);
}
if (username != null && !username.isEmpty()) {
return userService.getUserByUsername(username);
}
throw new MissingParameterException("...some message...");
}
#GetMapping()
public #ResponseBody
List<OutputUserDTO> getUsers() {
return userService.getUsers();
}
Of course I get an error, that is Ambiguous mapping.
I thought to always return a List so that I can merge the 2 endpoints and, in case you pass some parameters, return a Singleton... even though I don't know if it's a correct practice.
Or else, create one endpoint for each parameter, GET /users/{userId}, GET /users/{username}, ... but I don't like it neither (If I have 10 different way to get a user then I'll have to implement 10 endpoints :S)
What are some good practices in this case??
Thanks.
Replace MissingParameterException with return userService.getUsers();, and get rid of the other method, you know, the one with exactly the same mapping as the first method.
To make that work, you'd have to change return type to Object, which is not going to be a problem, since it's the actual object returned that controls the effect of #ResponseBody, not the declared type.
#GetMapping()
#ResponseBody
public Object getUserByParameter(#RequestParam(required = false) String id,
#RequestParam(required = false) String username) {
if (id != null && ! id.isEmpty()) {
return userService.getUserById(id);
}
if (username != null && ! username.isEmpty()) {
return userService.getUserByUsername(username);
}
return userService.getUsers();
}
FYI: #ResponseBody is a method-level annotation, so it should be listed before any keyword modifiers.
The Java Language Specification, section 8.3.1. Field Modifiers, says:
FieldModifier:
(one of)
Annotation public protected private
static final transient volatile
[...]
If two or more (distinct) field modifiers appear in a field declaration, it is customary, though not required, that they appear in the order consistent with that shown above in the production for FieldModifier.
It should be like #GetMapping("/users") on respective method
http://www.appsdeveloperblog.com/pathvariable-spring-mvc/
I suppose that the reason for that is, in getUserByParameter, both parameters are optional but if both the parameters are not passed it will conflict with your second getMapping.
more over, what is returned changes in the three scenarios. scenario 1 returns a list of DTOs while scenarios 2 & 3 return a single DTO
i dont think you can handle all three scenarios using your request path /users unless you want to wrap even a single DTO in a list, in which case you can simply merge your two methods. call getUsers() when both parameters are missing, in other cases, do what you currently do but wrap the response in a list.
if you want to keep them separate and return DTO or List, you should probably separate them out into /users and /user by specifying #GetMapping("/user") on method one and #GetMapping("/users") on method two
hope this helps
Lets assume I have a database table like:
Table Building
ID int (primary key)
title varchar
And to fetch this, I have a Java Class like:
class Building{
private Integer ID;
private String title;
...
}
(Add some JPA annotations in your imagination if that helps you).
But now, depending on the actual Building, I want to execute some logic.
My first thought was to create a big switch/case like:
switch(building.getId()){
case 1:
BuildingA buildingLogic = new BuildingA(building);
break;
case 2:
BuildingB buildingLogic = new BuildingB(building);
break;
...
}
buildingLogic.doBuildingSpecificStuff();
But that would end in an endless switch/case, which would not be very clear to "read".
My next thougt (already covered by the answer of Mark Elliot) was to write the actual Class name into the database as an additional field (BuildingClass) and with some (to me currently unknown) java magic, create the object from the building.getBuildingclass() String, but I already assume that would have some sort of disadvantages...
So I thought maybe you people could give me some more ideas or comments on my thoughts.
Supposing you did have the class name, you could use reflection to instantiate a new instance, though there are some security and performance risks for going down this path.
String className = "com.foo.bar.Baz";
// gets the actual class (might throw an Exception if the class doesn't exist)
Class<?> clazz = Class.forName(className);
// probably want to have some common interface these conform to, so
// let's assume you have one, you can test this is the right kind of class
CommonBuildingType buildingLogic = null;
if (CommonBuildingType.class.isAssignableFrom(clazz)) {
// get the constructor to invoke (might throw if no match)
Constructor constructor = clazz.getDeclaredConstructor(Building.class);
// safe to cast, we checked above
buildingLogic = (CommonBuildingType) constructor.newInstance(building);
} else {
// throw an exception?
}
You should use hibernate to achieve it more sophistically
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; }
}