So I have something like this in my driver class:
Person person1 = new Person(home1, job1);
Home home1 = new Home(person1);
Job job1 = new Job(person1);
where the parameters for the Person constructor are a home and job, and for the home constructor is the owner, and for the job constructor is the employee. How could I make this work?
Avoid circular dependencies where possible. However you can do this by wiring them up from the outside. So:
Person person1 = new Person();
Home home1 = new Home(person1);
person1.setHome(home1);
Job job1 = new Job(person1);
person1.setJob(job1);
As I say often unwise, but can be useful on occasion when used carefully. However it is always unwise to do this and make Person rely on .setHome(home1); and .setJob(job1); being called; in otherwords a homeless jobless Person should still operate without error.
You can't forward-reference the variable like your code is doing. When creating person1, home1 and job1 haven't been created yet. Because of the interdependency, this approach will always have something being passed to a constructor that isn't itself initialized yet.
You will need to create these objects without the references, then use setter methods after instantiation to create the relationships. Something like this:
Person person1 = new Person();
Home home1 = new Home();
Job job1 = new Job();
person1.setHome(home1);
home1.setOwner(person1);
person1.setJob(job1);
job1.setOnwer(person1);
First of all it's ain't pretty to do something like that, so the best solution will be rethinking solution :)
Anyway, if you really have to do something like that, you can move initialization from constructor to other method (for example setter). One of possible solutions:
public class Home {
// ...
public void setPerson(Person person) {
// ...
}
}
public class Job {
// ...
public void setPerson(Person person) {
// ...
}
}
public class Person {
// ...
public void Person(Home home, Job job) {
job.setPerson(this);
home.setPerson(this);
// ...
}
}
// ...
Home home = new Home();
Job job = new Job();
Person person = new Person(home, job);
Assuming Person is your main object (a person has a job and owns a home), you could, in its constructor, create a new Home and a new Job and pass them references to the person.
public class Person {
private Home home;
private Job job;
public Person() {
this.home = new Home(this);
this.job = new Job(this);
}
public Home getHome() {
return this.home;
}
public Job getJob() {
return this.job;
}
}
Your Home and Job classes will obviously have to take a Person in their constructors.
So back in your Driver, all you need is:
Person p = new Person();
Job j = p.getJob();
Home h = p.getHome();
And now you can do whatever you want to with all three.
This is not possible with just constructors - as objects cannot be used before they are created and variables cannot be used before they are assigned (meaningful) values! As such, mutators (or field access) must be used:
// No circular deps yet
Home home1 = new Home();
Job job1 = new Job();
Person person1 = new Person(home1, job1);
// Connect back to person
home1.setPerson(person1);
job1.setPerson(person1);
However, this manual assignment is prone to being forgotten or otherwise incorrectly applied. One way this parent-child relationship establishment can be cleaned up - and is done so in various UI or other Object Graph libraries - is to make the "add" method also establish the opposite relation. Then it can be "simplified" to:
Home home1 = new Home();
Job job1 = new Job();
Person person1 = new Person(home1, job1);
// Note that only one side of the dependency has been manually assigned above!
// Which in turn takes care of the appropriate logic to
// establish the reverse dependency ..
public Person (Home home, Job job) {
this.home = home;
this.home.setPerson(this);
// ..
}
It might also be prudent to add guards such that a Home/Job instance can't be "accidentally" re-assigned to a different person.
Related
// GameWorld
PooledEngine engine = new PooledEngine();
// Gun class
class Gun {
publc Gun(Entity entity) {
createComponents();
addComponents();
}
public Entity getEntity();
}
// Option 1
Gun gun = new Gun(engine.createEntity());
engine.addEntity(gun.getEntity());
// Option 2 or a simple method inside EntityFactory class
public Entity createGun() {
Entity gun = engine.createEntity();
// components creation here
..
engine.addEntity(gun);
return gun;
}
Question which one is the better way of creating an Entity made from the PooledEngine?
option 1, I create a new class called Gun then handle the creation of components there.
option 2, Inside the EntityFactory class add new method called createGun()
class EntityFactory {
// constructor
public void build() {
// Option 1
Gun gun = new Gun(engine.createEntity());
// where I can call gun.fire(), gun.reload(), gun.dispose, and so on..
engine.addEntity(gun.getEntity());
// Option 2
Entity gun = createGun();
engine.addEntity(gun);
}
public Entity createGun() {
Entity gun = engine.createEntity();
// components creation here
...
engine.addEntity(gun);
return gun;
}
}
EntityFactory factory = new EntityFactory(world, engine);
factory.build();
I'd prefer the second method via an EntityFactory.
Explanation:
Whereas the EntityFactory is a clean representation of the common factory pattern, you Gun looks like a builder pattern but with just one method for creating an Entity. So it makes no sense to use a new instance for every Entity creation as you store no building state in the instance.
Your Gun class is just a wrapper for an Entity. But if you get an Entityfrom engine.getEntitiesFor(Family... you cannot get it's Gun wrapper. So you cannot really use it for anything in your engine's systems.
If your EntityFactory gets too complex and long, I'd recommend to split it into a GunFactory, BananaFactory and so on.
Good morning/afternoon/evening,
I'm a Java beginner, but I've been tasked with making a database application.
In that application, I'd like to create a class that returns other objects when an instance of that class is created:
public LoadStudents(String query){
File studentsFolder = new File("Data/Students/");
try{
switch (query){
case "all": // additional cases might be added in the future
//- that's why I am using the class in the first place
for (final File entry : studentsFolder.listFiles()){
if(!entry.isDirectory()){
FileInputStream in = new FileInputStream(entry);
ObjectInputStream object = new ObjectInputStream(in);
Student[] student = (Student[]) object.readObject();
object.close();
}
}
break;
default:
final File entry = new File("Data/Students/"+query+".stud");
FileInputStream in = new FileInputStream(entry);
ObjectInputStream object = new ObjectInputStream(in);
Student searched = (Student) object.readObject();
}
}
catch(Exception load){
load.printStackTrace();
}
}
My question is: Can the student objects can be accessed when I create the LoadStudents object? If so, how?
LoadStudents load = new LoadStudents("something");
load.searched.doSomething();
maybe?
P.S. I'm sorry if this is a noobish question, but I'd like to know the answer nevertheless.
If the public methods of a class return Objects (any kind of Object) then other classes can use it without restriction. There are other rules, but that's the primary one to consider when beginning Java programming.
Also, I recommend that you adopt (in the beginning at least) a different style of organizing your solution.
Make your class names reflect things.
Make your method names on the class reflect what you can do with that thing.
so
public class Database {
.....
public Student getStudentById(String id) {
....
}
....
}
which then get used like so
Database database = ....
Student bob = database.getStudentById("2323");
or something like that.
With this approach, then you could add a "StudentListener" interface, like so
public interface StudentListener {
....
public void studentAdded(Details ...);
}
And make the database accept student listeners.
(in Database)
public void addListener(StudentListener listener) {
...
}
Then when a student addition is done calling the additional lines
for (StudentListener listener : listeners) {
listener.studentAdded(details);
}
will have the listeners be notified by the database that a student was added.
The pattern above is known as the Listener pattern, and you should learn it. It is very useful in many situations, and you will see it again.
Add Student searched object to class.
ex.
public Student searched;
In constructor set object to searched field. You dont need to create new object.
I'm using Jodd DbOom to manage my queries and it's really awesome. But right now I'm are facing an undocumented situation.
I have a query that returns a list of objects(A), and each A has a list of objects (B), and each B is joined with other objects(C, D, E, etc). The problem is that the class JoinHintResolver doesn't set the values C, D, E on the B objects. The B objects are set correctly on the A objects.
Below is a test method to reproduce the error. The other used classes(Girl, BadBoy) are from Jodd test packages.
public void testHintsList() {
Room room = new Room();
Girl girl = new Girl();
BadBoy badBoy = new BadBoy();
Object[] data = new Object[] { room, badBoy, girl };
JoinHintResolver jhr = new JoinHintResolver();
Object[] result = jhr.join(data, "room, room.boys, room.boys.girl");
assertEquals(1, result.length);
assertTrue(result[0] instanceof Room);
room = (Room) result[0];
badBoy = room.getBoys().get(0);
assertEquals(girl, badBoy.girl);
}
public class Room {
private Long id;
private List<BadBoy> boys;
public Room() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<BadBoy> getBoys() {
return boys;
}
public void setBoys(List<BadBoy> boys) {
this.boys = boys;
}
}
The documentation doesn't have any example like this, and Google neither. So I don't know if I did something wrong, or if Jodd wasn't prepared for this situation.
How could I set the hints so that Jodd would set the values correctly?
So the problem here is the fact that you have a collection of BadBoys in your Room. And the hint:
room.boys.girl
suggest they you want to inject a Girl instance into a collection. In java words, this is equivalent to:
getRoom().getBoys().setGirl(girl);
Obviously, since getBoys() returns a List, we can not set the girl property.
To test this what I've said, use the following hint instead:
room.boys[0].girl
This would inject the girl instance into the very first element of the list. Or, you can change your Room to have just a Boy property, instead of the list, and the original hint will work.
I hope this works for you :)
(see test)
EDIT
In this branch I have something that looks like a fix :) Now you can write something like:
select $C{room.*}, $C{room.boys:boy.*}, $C{room.boys.girl:girl.*}
from $T{Room room} join $T{Boy4 boy} on $room.id=$boy.roomId
join $T{Girl4 girl} on $boy.id=$girl.boyId
order by $room.id, $boy.id
And you can have the following model:
Room has list of Boy. Each Boy has one Girl assigned. When entityAware is on, this should work. Maybe you have time to test the branch?
I'm trying to develop an online hotel booking system. I have the main class which takes input from the user such as their name, their payment information, and other data fields and makes a Reservation object using that information. I have another class called Room that has a list of Reservations for each Room object. The problem I am having is I can't figure out a way to add the Reservation object into the list in the Room object. Here is some of the code:
public class HotelReservationSystem
{
private Reservation reservation;
public void makeReservation(int checkIn, int checkOut)//Other parameters
{
reservation = new Reservation(checkIn, checkOut);
}
}
public class Room
{
private ArrayList<Reservation> reservations;
public void addReservation(//parameters?)
{
reservations.add(//parameter?);
}
}
I don't know how to get the new Reservation object to be passed as a parameter for the add method in the Room class.
I just can't wrap my head around it and was hoping for someone to help jog my thinking process.
Thanks for your help.
Let makeReservation return the created Reservation object:
public Reservation makeReservation(int checkIn, int checkOut)//Other parameters
{
reservation = new Reservation(checkIn, checkOut);
return reservation;
}
(You could also create a getter for reservation)
Then change your addReservation like this:
public void addReservation(Reservation res)
{
reservations.add(res);
}
And then just add it like this:
HotelReservationSystem hrs = new HotelReservationSystem();
Reservation res = hrs.makeReservation();
Room room = new Room();
room.addReservation(res);
However, you might want to rethink your model. Right now your HotelReservationSystem is creating a reservation and only saves that one, overwriting old ones. What happens if you create more than one? Also how can you get the reservations for a certain room given the HotelReservationSystem object? Just some things to think about...
I believe you must have tried this
public void addReservation(Reservation reservation)
{
reservations.add(reservation);
}
but the problem here is that your list reservations is null and will throw null pointer exception. So better initialize it at declaration. So change this
private ArrayList<Reservation> reservations;
to
private ArrayList<Reservation> reservations = new ArrayList<Reservation>();
And in your makeReservation method of Hotel class do this:
Room room = new Room();
room.addReservation(reservation);
I have a method where I want to factor out some code into its own method
This is what I have:
public class TD0301AssignmentForm extends Form {
public TD0301AssignmentForm(TD0301AssignmentDAO dao, STKUser authenticatedUser) {
this.dao = dao;
this.authenticatedUser = authenticatedUser;
}
public Object insert(HttpServletRequest request) {
TD0301Assignment tdas = new TD0301Assignment();
TD0301Assignment tdas_orig = null;
Date dateNow = new Date();
try {
// Get the inuput from HTML form
tdas.setCalc_num(FormUtil.getFieldValue(request, FIELD_CALC_NUM));
processDate(request, tdas);
tdas.setCalc_dept(FormUtil.getFieldValue(request, FIELD_CALC_DEPT));
tdas.setYear_oi(Integer.toString(DateUtil.getIntYear(dateNow)));
processCalcSafetyRequirements(request, tdas);
...etc...
if (isSucces()) {
// Instantiate a base work flow instance!
WorkflowInstance wfi = new WorkflowInstance();
WorkflowInstanceDAO wfiDAO = new WorkflowInstanceDAO();
wfi.setWorkflow_class_id(tdas.getCalc_level());
wfi.setStarted_by(authenticatedUser.getBadge());
wfi.setStatus("0");
wfi.setLast_date(dateNow);
// Insert the WorkFlowInstance into the database, db sets returned sequence number into the wfi object.
wfiDAO.insert(wfi, authenticatedUser);
// Insert the TD0301Assignment into the db
tdas.setWorkflow_instance_id(wfi.getWorkflow_instance_id());
}
I'd like to remove the WorkflowInstance code out into its own method (still in this Class) like this:
if (isSucces()) {
insertWorkFlowInstance(request, tdas);
tdas.setWorkflow_instance_id(wfi.getWorkflow_instance_id());
but wfi is now marked by Eclipse as not available. Should I do something like this to fix the error so that I can still get the wfi.getWorkflow_instance_id() in the isSuccess block above? I know it removes the error, but I am trying to apply best practices.
public class TD0301AssignmentForm extends Form {
private WorkflowInstance wfi = new WorkflowInstance();
private WorkflowInstanceDAO wfiDAO = new WorkflowInstanceDAO();
Instance variables ("properties" or "fields") are not necessarily the way to go if they're not used throughout the entire class.
Variables should have the smallest scope possible--this makes code easier to reason about.
With some noise elided, and also guessing, it seems like the WorkflowInstance and WorkflowInstanceDao could be localized (names changed to match Java conventions):
public class TD0301AssignmentForm extends Form {
public Object insert(HttpServletRequest request) {
TD0301Assignment tdas = new TD0301Assignment();
try {
tdas.setCalcNum(FormUtil.getFieldValue(request, FIELD_CALC_NUM));
processDate(request, tdas);
tdas.setCalcDept(FormUtil.getFieldValue(request, FIELD_CALC_DEPT));
tdas.setYearOi(Integer.toString(DateUtil.getIntYear(dateNow)));
processCalcSafetyRequirements(request, tdas);
if (isSuccess()) {
WorkflowInstance wf = buildWorkflow(tdas);
tdas.setWorkflowInstanceId(wf.getId());
}
}
}
private buildWorkflow(TD0301Assignment tdas) {
WorkflowInstance wfi = new WorkflowInstance();
wfi.setWorkflowClassId(tdas.getCalcLevel());
wfi.setStartedBy(authenticatedUser.getBadge());
wfi.setStatus("0");
wfi.setLastDate(new Date());
WorkflowInstanceDao wfiDao = new WorkflowInstanceDao();
wfiDao.insert(wfi, authenticatedUser);
}
}
Whether or not this is appropriate depends on how/if the WorkflowInstance is used in the rest of the method snippet you show. The DAO is almost certainly able to be localized.
As methods become smaller and easier to think about, they become more testable.
For example, buildWorkflow is almost easy to test, except that the DAO is instantiated "manually". This means that testing the method will either (a) depend on having a working DAO layer, or (b) it must be mocked by a framework that can mock static utility methods (several can).
Without seeing all your code it's not easy to see exactlywhat you are trying to achieve. The reason eclipse is complaining is because it no longer has a wfi instance to play with because you've moved its local instance into your method, but creating another wfi instance is not likely to be your answer.
To get this working change the wfi to be class local and either use it's id directly or return wfi.getWorkflow_instance_id() from insertWorkFlowInstance() and then pass that value into tdas.setWorkflow_instance_id()