Win a 3 month subscription to Marco Behler Videos this week in the Spring forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

Design of Java Program: 2 Classes from same text or construction 2nd from 1st class  RSS feed

 
Ronald Hoovenaar
Ranch Hand
Posts: 53
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi guys. I am having some difficulties designing my model. I am making an event simulation model: basically creating and executing a series of event objects until all events have been executed.
These events make changes to objects of other classes, for instance on my class 'Order'. The base information about orders is available in a txt-file. Currently I am making objects of class Order AND objects of class OrderSubmitEvent from this same text file (see picture below). What I want my program to do is to execute all events that will occur for each order (so orderSubmitEvent, orderConfirmEvent, orderManufacturingEvent and several more). Each OrderSubmitEvent creates a OrderConfirmEvent. Each OrderConfirmEvent creates an OrderManufacturingEvent, etc. The time at which the events are executed, I want to save in my Order-objects (see ConfirmTime, ManufacturingTime).

I am stuck in how to design this, so I would like your help here. When I proceed in my current ways, making two classes from same text file, I think I am wrongly believing myself that the two orderID belong to the same object. I do not know whether it is possible to make both orderIDs belong to the same object.

I also thought about making only objects from the class Order. And then creating orderSubmitEvents during the creation of orders, see below:


What are your thoughts on this? What is desirable in terms of correct programming?
One final remark: Would I need to use a HashMap (or other mapping tools) to make sure that all event are connected to the right object?
 
Stephan van Hulst
Saloon Keeper
Posts: 7507
135
  • Likes 2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Design a class that has the responsibility of handling orders, maybe a Store? This class will accept orders, confirm them and manufacture them. Another class can then read the details from the file and submit them to the Store. The Store will generate events at the appropriate times, and pass them to listeners that have registered with the store. Instead of reiterating all the information that's already in the order, why not pass the order directly to the event handlers?

Make Orders and Events immutable. Override equals() and hashCode() on an Order, so you can compare them for equality. You can use a Map to store orders, using their ID as key.
 
Ronald Hoovenaar
Ranch Hand
Posts: 53
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think that your suggestion is very useful, but my knowledge of Java is only limited. Because of this, I do not fully understand everything you said.

You suggest the following:
The order.txt file is read by a new class, let's call it OrderReader. This reader makes Order objects for each line in the txt-file. These orders will be given a Hash-key. You suggest to make the Order-class immutable. Why?
Furthermore, a class named Store could handle all methods concerned with the events: thus data processing (adding the right information to the right variables of the right objects), creation of new events, etc.

What do you mean with "pass the order directly to the event handlers" and "passing events to listeners". I am not very familiar with the terms event handlers and listeners.
 
Stephan van Hulst
Saloon Keeper
Posts: 7507
135
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Orders are not given a hash-key. Orders create their own hashes based on their private information. This is what the hashCode() method does, which you should override for Order.

Types that are pretty much just information carriers should almost always be made immutable, because it makes handling them much easier. For instance, if OrderReader submits an Order to the Store, correct handling of the Order by the Store becomes much more difficult if the Order class is mutable, because the Store could 'misplace' the order if you change the Order from outside the Store object:
The store can prevent this from happening by making so called 'defensive copies', but making Order immutable instead is much more robust, and makes it easier for you to make correct assumptions about the code you're writing.

There are two sides to events: You need to fire events, and you need to handle events. Events are fired by whatever causes them, in this case, the Store. Events are handled by 'event listeners'. Without listeners, firing events is useless, because there's nobody to listen for them.

In your design, when an order has been confirmed, what would you like to happen in response to that event? To make that happen, you write a class that 'listens' for the event, and you register it with the Store. The store then lets your listener know the event happened, and the listener can respond to it inside its 'event handler'.
 
Campbell Ritchie
Marshal
Posts: 54909
155
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sander Hoovenaar wrote:I think that your suggestion is very useful, but my knowledge of Java is only limited. Because of this, I do not fully understand everything you said.

You suggest the following:
The order.txt file is read by a new class, let's call it OrderReader. . . .
In that case, stop at that stage. Do the reading, put all the lines read into a List<String> and print them. Verify that the right lines are being read in the right order.

Then go back and consider the next stage. Yes, it probably will be an Order object. Remember that you can get a Stream<String> from a buffered reader with its lines() methodYou can continue that process to create a List<Order> from the same Stream instead of the List<String>, if you so wish. This is the Collectors class, so you know what to write for its import.
 
Campbell Ritchie
Marshal
Posts: 54909
155
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephan van Hulst wrote:. . . The store can prevent this from happening by making so called 'defensive copies', but making Order immutable instead is much more robust, . . .
Also, if Order is immutable, defensive copies are unnecessary.
 
Ronald Hoovenaar
Ranch Hand
Posts: 53
1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:In that case, stop at that stage. Do the reading, put all the lines read into a List<String> and print them. Verify that the right lines are being read in the right order.

Then go back and consider the next stage. Yes, it probably will be an Order object. Remember that you can get a Stream<String> from a buffered reader with its lines() methodYou can continue that process to create a List<Order> from the same Stream instead of the List<String>, if you so wish. This is the Collectors class, so you know what to write for its import.


I had this reading mechanism already:

Will this suffice? If not, why not?
Why exactly do you suggest Collectors?
 
Stephan van Hulst
Saloon Keeper
Posts: 7507
135
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
  • Why is the method public?
  • Why does it return an ArrayList instead of just a List?
  • Don't use File if you don't have to, use java.nio.file.Path instead.
  • You're closing the reader incorrectly. Use try-with-resources.
  • Don't catch NumberFormatException. It's not thrown anywhere.
  • Don't catch Exception. It's too general, catch specific exceptions.

  • Collectors are useful for making a Stream into a Collection. BufferedReader has a method that does all the heavy lifting of reading lines from a file for you, and it returns them as a Stream.

    If all you're going to do with the lines is put them in a List, then you should be using java.nio.file.Files.readAllLines() instead. You can then ignore Collectors.
     
    Stephan van Hulst
    Saloon Keeper
    Posts: 7507
    135
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Another curious thing: why are you suffixing your variable names with numbers?
     
    Winston Gutkowski
    Bartender
    Posts: 10573
    65
    Eclipse IDE Hibernate Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Sander Hoovenaar wrote:The order.txt file is read by a new class, let's call it OrderReader. This reader makes Order objects for each line in the txt-file. These orders will be given a Hash-key...

    Stop right there.

    After all that lovely modelling work, you've leapt straight to implementation, so you're making all sorts of assumptions that don't need to be made ... YET.

    1. Why should Orders come in by a .txt file? Now this may be a class exercise, and you may have been told that they will, but in real life an order might come in via a text file, a servlet request, a JSON object, or any number of ways from another system upstream of yours. So make sure that whatever conversion process you write for your text file can be easily replicated for other forms of input.

    2. Why a hash key? That sounds like you've already decided how they're going to be stored. Hold off on decisions like that until you absolutely have to make them.

    The time at which the events are executed, I want to save in my Order-objects (see ConfirmTime, ManufacturingTime)

    Really? I would have though that the best place to store an "action time" or "completed time" would be in the Event being actioned.

    That doesn't stop you from creating a method (or methods) to return those times though.

    You've obviously spent some time modelling the information that you need, but objects aren't just about information.
    Now you have to start thinking about what they need to do (have a look at the link).

    For example: You have three Event types.
    Are they sequential? - ie, does each Event rely on the completion of a "previous" one?
    Is there any information that needs to be passed from one Event to another?
    Do Events themselves have any lifecycle (at least that you're worried about)?

    My advice: Get out a LOT of paper, and a pencil, and do a lot of thinking and scribbling before you write your first line of code.

    Finally, one small information thing I noticed: Every single one of your classes contains a "location", made up of latitude and longitude.
    That's a perfect candidate for a Java class.

    HIH

    Winston
     
    Ronald Hoovenaar
    Ranch Hand
    Posts: 53
    1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Stephan van Hulst wrote:
  • Why is the method public?
  • Why does it return an ArrayList instead of just a List?
  • Don't use File if you don't have to, use java.nio.file.Path instead.
  • You're closing the reader incorrectly. Use try-with-resources.
  • Don't catch NumberFormatException. It's not thrown anywhere.
  • Don't catch Exception. It's too general, catch specific exceptions.

  • Collectors are useful for making a Stream into a Collection. BufferedReader has a method that does all the heavy lifting of reading lines from a file for you, and it returns them as a Stream.

    If all you're going to do with the lines is put them in a List, then you should be using java.nio.file.Files.readAllLines() instead. You can then ignore Collectors.


    1. I want to invoke this method in my main(). Would you suggest a different approach?
    2. Just read something briefly about the differences. Did only learn about ArrayLists so far. It made sense to me to return an ArrayList, since I started the method by making an ArrayList. Why is it possible to return a different type of List and why bother? I got the following now, and it works:

    3. What resource/object should close the block then?
    4. Changed it to path
    5. Done
    6. Done

    Stephan van Hulst wrote:Another curious thing: why are you suffixing your variable names with numbers?

    At some point during my programming, I suffixed them to ensure that several readers in the same program did not mix up. Later I found that those are local variables ( ) and I distributed the reading methods over other classes.
     
    Ronald Hoovenaar
    Ranch Hand
    Posts: 53
    1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Thank you Winston for your reply. I may indeed have leaped too much into implementing already. I am aware that I need to have a good design before doing this. For that reason, I asked this question here

    1. This model will be used with txt-files only. That is a given.
    2. With the HashKey I wanted to make sure that I execute methods on the right objects. I assume(d) that this one is the best method to do this.

    For the events. Most are sequential, need to pass orderID and eventTime. If latitude and longitude are stored in an immutable class, then I can easily request these because these latitudes and longitudes are fixed. I do not have to carry those forward to each separate subsevent.

     
    Stephan van Hulst
    Saloon Keeper
    Posts: 7507
    135
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Sander Hoovenaar wrote:1. I want to invoke this method in my main(). Would you suggest a different approach?

    Yes, make it package private. It can be used by your main() method as long as it's in the same package. Try to minimize the use of the public and protected access specifiers as much as sensible.

    2. Just read something briefly about the differences. Did only learn about ArrayLists so far. It made sense to me to return an ArrayList, since I started the method by making an ArrayList. Why is it possible to return a different type of List and why bother?

    Let's turn this logic around. Why commit to a specific kind of list if all you're interested in is that it's a List, no matter what kind of list exactly?

    3. What resource/object should close the block then?

    The reader is the resource. If you use it in a try-with-resources statement, it's automatically closed in a fool-proof way. It would look like this:

    However, heed Winston's advice on skipping to implementation too fast.

    At some point during my programming, I suffixed them to ensure that several readers in the same program did not mix up. Later I found that those are local variables ( ) and I distributed the reading methods over other classes.

    Then you should probably clean that up.
     
    Winston Gutkowski
    Bartender
    Posts: 10573
    65
    Eclipse IDE Hibernate Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Sander Hoovenaar wrote:1. This model will be used with txt-files only. That is a given.

    OK, but keep in mind what I said above.

    2. With the HashKey I wanted to make sure that I execute methods on the right objects. I assume(d) that this one is the best method to do this.

    There's that horrible word "assume" again.

    Design 101: DON'T assume anything. Particularly about how things will be done.

    Also: I notice you have an orderID. If this is unique, doesn't that also make it a hashcode?

    If latitude and longitude are stored in an immutable class, then I can easily request these because these latitudes and longitudes are fixed. I do not have to carry those forward to each separate subsevent.

    I'm not quite sure whether that means you agree with me or not. All I'm saying is that with a:and the relevant constructor and getters, you only have to have one member in any class that contains a "location".

    Winston
     
    Stephan van Hulst
    Saloon Keeper
    Posts: 7507
    135
    • Likes 1
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Winston Gutkowski wrote:Also: I notice you have an orderID. If this is unique, doesn't that also make it a hashcode?

    As long as equality is not based solely on the orderID.
     
    Winston Gutkowski
    Bartender
    Posts: 10573
    65
    Eclipse IDE Hibernate Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Stephan van Hulst wrote:
    Winston Gutkowski wrote:Also: I notice you have an orderID. If this is unique, doesn't that also make it a hashcode?

    As long as equality is not based solely on the orderID.

    OoooK; but if you can have two different orderIDs based on the same order, that suggests to me that the name is wrong.

    Winston
     
    Stephan van Hulst
    Saloon Keeper
    Posts: 7507
    135
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    It's just a safety measure. There's nothing stopping anyone from making two orders that have the same ID but with some of the other properties being different. If these objects are then considered equal based only on their ID, you're setting yourself up for a world of hurt.
     
    Winston Gutkowski
    Bartender
    Posts: 10573
    65
    Eclipse IDE Hibernate Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Stephan van Hulst wrote:It's just a safety measure. There's nothing stopping anyone from making two orders that have the same ID but with some of the other properties being different. If these objects are then considered equal based only on their ID, you're setting yourself up for a world of hurt.

    I disagree. Customers may have repeat orders which are indistinguishable from each other by anything but an ID - or some sort of date/time stamp, which amounts to the same thing. There are many things in the world of information - including people - that require an ID for determination, since no other attribute, or group of attributes, is sufficient to uniquely identify the object.

    In fact, I'd say - in some cases - that you're setting yourself up for a world of hurt if you DO rely on anything else.

    Winston
     
    Stephan van Hulst
    Saloon Keeper
    Posts: 7507
    135
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    I think you're misinterpreting me. I'm not arguing against IDs. In this case, it's absolutely vital. I'm arguing that an ID shouldn't be the only thing to base equality on.
     
    Winston Gutkowski
    Bartender
    Posts: 10573
    65
    Eclipse IDE Hibernate Ubuntu
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Stephan van Hulst wrote:I'm arguing that an ID shouldn't be the only thing to base equality on.

    But what else would you base it on? If you have two different Order objects with the same ID in your program (unless one is simply a "dummy" object - for example, to check if a Set contains the Order), then something is seriously wrong.

    Maybe I'm missing something here, but my Order.equals() method would be basically this.ID == that.ID.
    I suppose you could write something like an equivalent() method to see if the contents are the same, but I certainly wouldn't use it to bar/allow storage in a collection.

    Winston
     
    Stephan van Hulst
    Saloon Keeper
    Posts: 7507
    135
    • Mark post as helpful
    • send pies
    • Quote
    • Report post to moderator
    Sorry for the late reply, I was on a little holiday to Germany

    I would base the equals() method not only on the ID, but also on the other order details.

    The reason is that if you by mistake made two orders with the same ID but different order details, and you would keep orders in a set, then it's possible to lose information without noticing if you only check the ID. Otherwise you will have two orders with the same ID in your set, which also is a mistake, but the mistake is more apparent.

    Besides that, if somebody gave me two order receipts that look like below and asked me if the orders are conceptually equal, I would respond no.

     
    • Post Reply Bookmark Topic Watch Topic
    • New Topic
    Boost this thread!