A person has an ID.
A student has a student's ID.
A driver has a driver's license.
A person attends a school and becomes a student.
A student graduates and becomes an ordinary person.
A person gets a driver's license and becomes a driver.
A student gets a driver's license and becomes a driver.
Are those things just state change like in the following example:
class Person {
ID id;
StudentID stId;
DriverLicense license;
void drive() {
if(license == null) //illegal state exception
//drive
}
//bla bla
}
Or is there inheritance? Since an object is data+behavior, new data and being able to do new things should warrant a new object
class Student extends Person {
//
}
class Driver extends Person {
//
}
//things get messy here, in Java you can't extend multiple class
//what if there's a rule that, student drivers can request/get a tax reduction?
class DriverStudent extends Person, Driver {
//
}
PLUS, more importantly, how does one become the other? Through methods or constructors or 3rd classes(like a service or aggregate) ?
class Person {
Driver getADriversLicense() {
//create and return a Driver
//this person still exists but now there's a driver with this person's data
}
}
or:
class Driver extends Person {
public Driver(Person p) {
//constructor
}
}
or:
class Aggregate {
Driver giveDriversLicense(Person p) {
// access internal state of both objects(ditch encapsulation) and return a driver?
// put aggregate in same package with Driver and Person and use package private methods to provide encapsulation?
}
}
A better way to see such relationship is through roles i.e. a Person can play multiple roles .
How about a scenario where a Person can be a Driver and a Student and may be an Employee too ?
So IMO its best represented as following -
class Person{
List<Role> currentRoles ;
List<Role> getCurrentRoles(){
return currentRoles ;
}
public void addRole(Role role){
currentRoles.add(role) ; // so on
}
}
Using generics and type safe casts you can easily retrieve a specific role and invoke a related operation on it.
public interface DriverRole implements Role {
License getDriversLicense() ;
}
Edit : Taking it further to answer your question fully it easily addresses a scenario where Person gains or loses a Role i.e. add or remove a Role. As pointed out in the comment here this relationship is best represented through Has <0..M> relationship then IS - kind of relationship.
Edit 1 In comparison when you use a Decorator pattern your origin gets wrapped and too many decorators can create a aggregation chain which IMO is not an ideal scenario or alternatively it will result in a decorator inheritance chain which is not to my liking.
Having said that depending on a specific scenario one particular pattern might fit better then the other though in the example you have given I think a simple aggregation of Roles is best.
Inheritance is one way to think of these relationships but this only practically makes sense when it's reasonable to enumerate the number of combinations of statuses the person might have.
Another to think of this without constraining yourself through inheritance is to think of everyone as people with different credentials.
Rather than a person being a driver, think of everyone as a generic Person having a set of credentials. eg. a drivers license, and also having a student ID. Then you can represent all of these different credentials that a person may have through a Credentials class (which Driver, Student, etc) extend. A person may have a List which you can use to perform any case based logic that you might want.
As you mentioned, consider using inheritance to approach this problem. Each Student and each Driver are both considered a Person. Therefore, both Student and Driver should inherit functionality from the parent class, Person. As far as whether someone is a student, driver, both, or just an "ordinary person" should simply be stored in a variable.
public class Person
{
private int id;
private String type;
public Person(int id, String type)
{
this.id = id;
this.type = type;
}
public int GetID()
{
// Using *this* keyword for consistency, but not necessary here.
return this.id;
}
public void SetID(int id)
{
this.id = id;
}
public String GetType()
{
// Using *this* keyword for consistency, but not necessary here.
return this.type;
}
public void SetType(String type)
{
this.type = type;
}
}
public class Student extends Person
{
super(int id, String type);
}
public class Driver extends Person
{
super(int id, String type);
}
public class Main
{
public static void main(String [] args)
{
Student sam = new Student(342918293, "student");
Driver rosa = new Driver(147284, "driver");
}
}
I'n not too fond of this being an inheritance, as a Person can be a Driver AND a Student at the same time.
Though I don't fully rememeber how the decorator pattern (sugested by Mr. Poliwhirl) works, that may be the correct approach.
Related
I'm developing a hobby project to properly understand encapsulation, what classes can be responsible for, and rules. I asked for a code review and assistance in another forum, but I don't agree with the approach given.
I have the following requirements:
An international student requires documents to complete the registration process, but domestic students don't.
StudentStatus Interface:
public interface StudentStatus {
Collection<String> retrieveDocuments();
StudentType retrieveStatus();
}
public final class Domestic implements StudentStatus {
private final StudentType type;
private final Collection<String> documents;
public Domestic() {
this.type = StudentType.Domestic;
this.documents = Collections.emptyList();
}
#Override
public Collection<String> retrieveDocuments() {
return this.documents;
}
#Override
public StudentType retrieveStatus() {
return type;
}
}
public final class International implements StudentStatus {
private final StudentType type;
private Collection<String> documents;
public International(Collection<String> documents) {
this.type = StudentType.International;
this.documents = Collections.unmodifiableCollection(documents);
}
#Override
public Collection<String> retrieveDocuments() {
return Collections.unmodifiableCollection(documents);
}
#Override
public StudentType retrieveStatus() {
return type;
}
}
Student class:
public final class Student {
//left out constructor and getters for other attributes.
public Collection<String> retrieveDocuments() {
return status.retrieveDocuments();
}
public StudentType retrieveStatus() {
return status.retrieveStatus();
}
public boolean isVerified(StudentType type) {
return this.retrieveStatus() == type;
}
}
University class:
public class University {
private final Map<Student,Collection<String>> registeredStudents;
private final StudentType type;
public University()
{
registeredStudents = new HashMap<Student,Collection<String>>();
type = StudentType.International;
}
public void add(Student student){
if (student.isVerified(type)){
registeredStudents.put(student, student.retrieveDocuments());
}else {
//throw an exception or handle error accordingly
}
}
}
Before I continue, I understand that this is a really over simplified application process. In the real world, a lot more has to happen before a Student can register. The student may have to go through entrance exams, and payment before registration begins. Also, in a realistic environment, this information would probably be stored in a database that the campus employees can access.
In the other forum, the conversation went into what information is being given out, and approaches were given.
Have a rule class, that takes the Student object and verifies that it
is in fact international and has documents.
The problem I have with this, is you're still going to have to ask the Student his/her status either with the retriveStatus() or isVerified(), I don't really see how to do it any other way.
Pass the Student and collection of documents separately to be added to the Map.
In the real world, the University set the rule as stated above and it's responsibility is to check if International students have documentation.
When I suggested the approach above with the add(Student student) they stated it wasn't a good idea because the rules can change, and you'll have to change the Student class as well as the University class.
However, in the real world, a student is well aware of his/her status and if he/she is domestic/international and in possession of documents that can be given to the school.
Given the above approach, is writing the add method this way a good idea? Is there a better approach than the add method?
tl;dr - If a Student has to follow the rules set by the University, how then would the Student object communicate with the University to get the data so that the University can ensure the student object is complying with the rules without breaking encapsulation?
The conversation in previous post was probably leading you in a generally good direction. The principle that applies most is the Open / Closed principle. https://en.wikipedia.org/wiki/Open/closed_principle.
Don't set yourself up to have to constantly modify a particular class or set of classes (in OO world at least) in an area you know is going to be a frequent vector of change. The principle applies equally in the functional world, but your example is using an OOPL.
Little hand-built rules engine is a pretty good solution for your stated problem. Particularly if you know the rule flows on pretty fixed inputs - like the University and the Student. DocumentsRequiredForInternationalStudents is a rule class in that architecture - only needs to change if something about that rule itself changes. New rule, which is going to happen a lot = add new class, not modify existing one.
Sometimes you don't know vector of change, harder to make decisions, but if it's obvious, don't architect a system where you'll have to violate open/closed constantly due to an known change vector.
There are different ways to implement little rules engines. One option (this is crappy pseudo-code so it takes less space)
interface RegistrationRule
boolean isRegistrationValid(Student student) //might need university too for some rules.
class DocumentsNeededForInternationalStudents implements RegistrationRule
boolean isRegistrationValid(Student student)
// return student.status is international and student has documents, or student status is domestic
// (this rule passes through as valid any domestic students).
class RegistrationRules
// (holds all the rules you will use - kind of a factory)
constructor -> add to static list of rules an instance of all your rules
boolean runRulesForStudent(Student)
//iterate through all rules, call isRegistrationValid, short circuit and return false if one of them false
class University
addStudent(Student student)
if (RegistrationRules.runRules(student).... else
That's just one way to throw it together, but you can see it's not really a lot of code. You have an interface, an implementation class for each rule, and a little rules engine class that applies each rule for you. New rule = new class, modify your constructor in the rules engine to add an instance of that class, done.
This pattern begins to struggle a bit when the properties and behavior that are needed in the rules are very diverse and not concentrated in the same small set of classes.
You said:
In the real world, the University set the rule as stated above and
it's responsibility is to check if International students have
documentation.
This means that the verification responsibility lies on University (and can be different for each University) and not the Student. All student can do is provide necessary information for University to verify.
The add method should get documents from retrieveDocuments and run through its rules to determine is student is allowed to be accepted.
I'm using Spring Data Neo4j and I'd like to pull a list of owners. An owner can be a :Person (human) or an :Entity (company). I'm not sure what Type<T> should be. I could use in the GraphRepository<Type> interface.
My Query is:
MATCH ()-[r:OWNED_BY]->(o) RETURN
And this is the code I tried:
public interface Owners extends GraphRepository<Object> {
#Query("start o=MATCH ()-[r:OWNED_BY]->(o) RETURN o;")
Iterable<Object> getOwners();
}
I had an idea that I could perhaps extend a common base class, such as PersistentBaseObject with an id and a name, or an interface like HasIdAndName. Not sure how I'd integrate this though,
Yes,you could extend a common base class, perhaps like this-
public class Owner {
Long id;
String name;
...
}
public class Person extends Owner {
private int age;
...
}
public class Entity extends Owner {
private String location;
...
}
And add a matching repository for Owner
public interface OwnerRepository extends GraphRepository<Owner> {
}
which will allow you to do stuff such as ownerRepository.findAll()
But since you're using a #Query, there is no reason you can't put this method on even the PersonRepository (at least in SDN 4. I'm not sure about SDN 3.x)
#Query("start o=MATCH ()-[r:OWNED_BY]->(o) RETURN o;")
Iterable<Owner> getOwners();
Note however, that now your Person and Entity nodes are going to have an extra Owner label.
EDIT:
The additional label can be avoided by changing Owner to an interface.
Then the #Query on a repository returning a collection of Owner should still work.
Whole day I have tried to find a answer on the question:
"How to add auto-Incrementing "id" field in an Entity class?".
I am using Morphia (a type-safe java library for Mongo DB). After a couple hours of digging in source code and googling I have found a LongIdEntity class in org.mongodb.morphia.utils package. Based on this class I have implemented the following solution. See below:
City class:
#Entity
public class City {
#Id
private Long id;
}
Hotel class:
#Entity
public class Hotel {
#Id
private Long id;
}
CityLongIdEntity class:
public class CityLongIdEntity extends LongIdEntity {
public CityLongIdEntity(Datastore ds) {
super(ds);
}
}
HotelLongIdEntity class:
public class HotelLongIdEntity extends LongIdEntity {
public HotelLongIdEntity(Datastore ds) {
super(ds);
}
}
DAO implementation:
CityDAO class:
public class CityDAO extends BasicDAO<City, Long> {
public CityDAO(Datastore ds) {
super(ds);
}
#Override
public Key<City> save(City c) {
if (c.getId() == null) {
CityLongIdEntity ent = new CityLongIdEntity(getDs());
getDs().save(ent);
c.setId(ent.getMyLongId());
}
return getDs().save(c);
}
}
HotelDAO class:
public class HotelDAO extends BasicDAO<Hotel, Long> {
public HotelDAO(Datastore ds) {
super(ds);
}
#Override
public Key<Hotel> save(Hotel h) {
if (h.getId() == null) {
HotelLongIdEntity ent = new HotelLongIdEntity(getDs());
getDs().save(ent);
h.setId(ent.getMyLongId());
}
return getDs().save(h);
}
}
Or you can see all this code on the Github
The UML diagram is also available:
All this code works as expected and I am happy, but I have a couple questions:
As you can see, for each Entity I need to create additional Entity, for example: for entity City I created CityLongIdEntity (this entity is crucial part of auto-incrementing functionality) . In this case, if my app will have 20 Entities (City, Address, Hotel, User, Room, Order etc.) I will need to create a 40 classes! I am afraid, but I think it will be "Code smell". Am I right?
Also, the Entity doesn't know about EntityNameLongIdEntity and EntityNameLongIdEntity has no idea who is Entity. And only specific EntityDAO class combines ans uses those classes together. Is it ok? Or it is again code smell?
Each EntityDAO class overrides extends BasicDAO class and overrides method save(). The difference between overrided methods save() for different DAO classes is minimal. I am afraid. that is code duplication and code smell again. Am I right?
please provide your opinion.
We require numeric IDs on some entities, but our implementation is a little different:
We use a regular ObjectId on all entities. Where required, we add a numeric ID.
There is a dedicated AutoIncrementEntity, which keeps a counter for different keys — that would be your class name.
We don't use DAOs but a generic save method, where we check if we have an instance of a class with a numeric ID. If that ID hasn't been set, we fetch one and update the AutoIncrementEntity. The relevant method isn't used at the moment — let me know if it's totally unclear and I'll finish that code.
Two more things from my implementation, which might be a little confusing:
You can always provide a starting number, so our numeric IDs could be 1000, 1001, 1002,... instead of 1, 2, 3,...
The key in the AutoIncrementEntity isn't required to be a class, it could also be a subset. For example, you want to number employees within a company, then the key for employees of company A would be employee-A, for company B company-B,...
I have few issues/doubts to fill values in a HashMap
I want a HashMap to accept "Student" as key and "Details" as value.
Since key to a hashMap should be immutable I have some doubts how can this be dealt if
Student class did not cloneable
Student class has reference to which in turn have reference to "Lab"
public class Student {
private String id;
private String name;
private Department dept;
public Student(String id, String name, Department dept)
{
this.id=id;
this.name=name;
this.dept=dept;
}
public Department getDepartment()
{
return this.dept;
}
}
public class Department {
private String deptId;
private Lab lab;
public Department(String deptId, Lab lab)
{
this.deptId=deptId;
this.lab=lab;
}
public void setLab(Lab lab)
{
this.lab=lab;
}
}
public class Lab {
private String labId;
private String labName;
public Lab(String labId, String labName)
{
this.labId=labId;
this.labName=labName;
}
}
public class StudentDetails
{
private String fatherName;
private String address
public StudentDetails(String fatherName, String address)
{
this.fatherName=fatherName;
this.address=address;
}
}
public class StudentMaintainer {
public static void main(String[] args)
{
StudentDetails stDetails= new StudentDetails("John","Mumbai");
Lab lab= new Lab("100","CS");
Department dept= new Department("900", lab);
Student st = new Student("3000",dept);
Map<Student,StudentDetails> studentMaintainer= new ArrayList<>();
studentMaintainer.put(st,stDetails);
}
}
Now Even if Student is cloneable, I can get reference of Department and call setLab() which changes the StudentObject. (Am I wrong?)
Now if Department and Lab are from 3rd party jars, how can I use Student Object in my Map if Student hashCode is (primeNumber+Student.id+Department.id+Lab.id).hashcode() [just some weird case];
Immutability has nothing to do with Cloneable as far as I understand it, and in fact just the opposite. Immutability has more to do with declaring the class final and using immutable fields, non-overridable methods, no setter methods, getter methods that return deep copies of fields or immutable fields, etc... Please read A Strategy for Defining Immutable Objects for more on this.
Also your code has a pseudo-constructor:
public void Student(String id, String name, Department dept)
{
this.id=id;
this.name=name;
this.dept=dept;
}
A true constructor should not be declared to return anything, not even void. Better would be:
// note the difference?
public Student(String id, String name, Department dept)
{
this.id=id;
this.name=name;
this.dept=dept;
}
Also, your Student class should properly override equals and hashCode if it is to work well as a key for a HashMap.
Now Even if Student is cloneable, I can get reference of Department
and call setLab() which changes the StudentObject. (Am I wrong?)
You are correct. This can happen, and can cause your Student class to appear to be mutated. For an instance of Student to be immutable, you must not be able to modify any of its fields[0]. That includes calling something like a setter method on one of its fields.
Now if Department and Lab are from 3rd party jars, how can I use
Student Object in my Map if Student hashCode is
(primeNumber+Student.id+Department.id+Lab.id).hashcode() [just some
weird case];
That is a very good question. You obviously can't just change the classes to be immutable, since you don't have control over them, so you may have to get a bit creative. Possible solutions:
if the third party objects you want to use are interfaces, you could implement the interface with your own type, where the body of every mutator method throws an exception (think e.g. java.util.Collections.unmodfiableList). This has the benefits that you can still refer to the third party class in your codebase, but the drawback that calling mutator methods will fail at runtime, rather than at compile time.
write adapters in your own codebase, like this:
public final class MyImmutableDepartment {
private final MyImmutableLab lab;
private final String departmentId;
public MyImmutableDepartment(Department thirdPartyMutableDepartment) {
this.departmentId = thirdPartyMutableDepartment.getId();
this.lab = new MyImmutableLab(thirdPartyMutableDepartment.getLab());
}
// getters and the MyImmutableLab class left as an exercise
}
This has the advantage that you know at compile time, the classes cannot be mutated.
The downside in both approaches is that you basically have to mirror every class from the third party library, to ensure they're immutable.
I don't think there's any other alternatives.
[0] there are some cases where this is possible, and can be used for internal caching, but it's a decent guideline to stick to when learning.
Student does not need to be immutable! Specifically, the requirement is that the behavior of equals/hashCode doesn't change when the key is in the HashMap.
This can be accomplished in three ways:
Don't implement equals/hashCode. If you use the default reference equality, it doesn't matter how you mutate the key. To clarify intent, override those methods, explicitly call super.equals, and make them final.
Don't include any fields that will mutate in your calculation of equals/hashCode. Or, if the properties of a field may change but not the reference, use reference equality (==) instead of field.equals() and call System.identityHashCode(field) instead of field.hashCode()
Don't mutate the object while it is used as a key in the HashMap. A bit dangerous, but works fine if references aren't held by code beyond your control. Document the requirement.
But, in your concrete example, each student has an id. Why would you use any other properties when implementing equals/hashCode.?
I'm trying to follow code-to-interface on a project. Should I be creating an interface first then implementing that interface for entity classes? I'm thinking this might be taking the interface first approach too far and entities should be ignored. This is what I mean...
public interface Address {
public String getStreet();
public void setStreet(String street);
}
#Entity
public class AddressImpl implements Address {
private String street;
public String getStreet(){
return this.street;
}
public void setStreet(String street){
this.street = street;
}
}
#Entity
public class OfficeImpl /* implements Office */ {
private Address location;
public Address getLocation(){
return this.location;
}
public void setLocation(Address location){
this.location = location;
}
}
public class Driver {
public static void main(String[] args) {
Office work = new OfficeImpl();
Address workAddress = new AddressImpl();
workAddress.setStreet("Main St.");
work.setLocation(workAddress);
}
}
I think creating Interfaces for Entities is probably not necessary.
The purpose of creating Interfaces (or at least, one of the purposes) is to make it easier to swap out one concrete implementation in favour of another. This is obviously a good thing for your DAOs, Business Logic etc.
But unless you have plans for the implementation of your entities to change as well, I would avoid it!
In your example, you are probably taking it too far, but once you add methods, write test cases and possibly use dependency injection, it will make more sense.
For simple projects like this, it is overkill, but once you get into a 'real' application, then it is often a good idea. Just be careful not to overdo it, everything doesn't need to implement an interface, just where it makes sense.
the interface for Entities should be the behaviors and properties that are common to all Entities!
public interface IEntity
{
int EntityId { get; set; }
bool FindById(int id);
bool Create(object [] values);
bool Delete(int id);
//etc.
}
sorry for the C# example, but the language doesn't matter. Interfaces are for 'plug compatability'.
I think when you're talking about entities, it's probably overkill.
Interfaces are useful when you're working with entities that have a common usage, but aren't necessarily the same. Can't think of a good way to explain it, but here's an example:
interface IFlaggable {
bool IsFlagged ...
string Reason ...
}
class ForumPost implements IFlaggable { }
class PrivateMessage implements IFlaggable { }
Hope that helps!
I generally don't make interfaces for data holding beans, that is I don't make interfaces for classes with primitive type values and getters/setters for them. Haven't really ever hit a moment where I would've needed interfaces for anything I usually use them for (polymorphism and mocking, mostly) so I haven't bothered doing that.
I guess I should point out that most of the time when I use databeans I also reflect the values from those same objects with custom classes which work like this:
Reflector r = new Reflector(new DataBean( [ values given through constructor ] ));
long someNumber = r.get("method", Long.class);