3
\$\begingroup\$

I want to model this scenario in object oriented language:

  • A hospital has number of patients
  • A patient can have zero or more medical conditions
  • A medical condition is identified by name and date

Please review my code and tell me:

  1. What can I do to incorporate object oriented fundamentals like reusability and extensibility?
  2. Do I need to implement an equals method in the MedicalCondition class?
  3. Do I need to initialize all the instance variables in constructor?
import java.util.Date;
import java.util.*;
import java.text.*;
class Patient
{
    private String name;
    private String streetAdress1;
    private String phoneNumber;
    private String streetAdress2;
    private String zipCode; //I really dont need to preform any numerical operations.
    private List<MedicalCondition> mcList;

    public Patient(String name) //Public for constructor.
    {
        this.name = name;
        mcList = new ArrayList<MedicalCondition>();
    }  

    public void setName(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }


    //All setter and getters

    public void addMedicalCondition(MedicalCondition mc)
    {
        mcList.add(mc);
    }

    public void deleteMedicalCondition(MedicalCondition mc)
    {
        mcList.remove(mc);
    }

    public List<MedicalCondition> getMedicalConditions()
    {
        return mcList;
    }

    public String toString(){
        return name;
    }
 }   

class MedicalCondition
{
      private String nameOfCondition;
      Date dateOfReport;

      public MedicalCondition(String nameOfCondition,Date dateOfReport)
      {
          this.nameOfCondition = nameOfCondition;
          this.dateOfReport = dateOfReport;
      }

      public String toString()
      {
          return nameOfCondition;
      }
 }


 public class HelloWorld{

     public static void main(String args[]) throws ParseException{
        Patient p1 = new Patient("Mike");

        DateFormat format = new SimpleDateFormat("mm/dd/yyyy");
        MedicalCondition mc1 = new MedicalCondition("Diabetes",format.parse("03/23/2000"));
        p1.addMedicalCondition(mc1);

        System.out.println(p1);
        for(MedicalCondition mc : p1.getMedicalConditions())
        {
            System.out.println(mc);
        }

        p1.deleteMedicalCondition(mc1); //Delete the condition

         System.out.println(p1);
        for(MedicalCondition mc : p1.getMedicalConditions())
        {
            System.out.println(mc);
        }
     }
}
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Bracing style

You started off with Allman-style bracing approach, but switched to the Java convention for HelloWorld and its main() method. Regardless of the styles you choose, please be consistent on this front. :)

Medical condition dates

Your medical conditions carry a start date, but should they also have an end date? Or are we only talking about incurable medical conditions here? While we're at it, Java 8 has new 'Time' APIs with a different (and better) approach to modeling chronology, so you may want to use the newer LocalDate class.

OK, now I get it... you delete medical conditions when they are cured. If you need to preserve medical history, you now know what you can do too...

Throwing Exceptions

public static void main(String args[]) throws ParseException

I understand HelloWorld is something like a test class to see your implementation in action, but still try to avoid doing something like this, especially when it's your main() method. One wrong input will simply cause your test class to quit with a stacktrace. It will be better if you have a method to handle the parsing and any possible errors. For example, if you want to default to the current date in case of parsing errors:

private static LocalDate parse(String date) {
    // using the new time APIs
    try {
        return DateTimeFormatter.ISO_LOCAL_DATE.parse(date, LocalDate::from);
    } catch (DateTimeParseException e) {
        return LocalDate.now();
    }
}

equals() implementation

Do I need to implement an equals method in the MedicalCondition class?

Your call. :) The longer answer is, what does it mean when a medical condition is-equal to another? Same condition and date? How helpful will such an equivalence be? To 'group' patients by the same medical conditions? If you don't have a strong case for this, you probably wouldn't want to implement it first.

Adding medical conditions

In this regard, I will recommend your existing approach over @Amr Ayman's answer. This is mainly because your Patient class should know what it means to add a medical condition, instead of letting callers of your Patient instances 'figure that out'. A better refinement is to wrap the instance returned by getMedicalConditions() with a Collections.unmodifiableList(): this will prevent callers of the method to modify the list's contents directly, such as wiping them outright.

Actually, on this note, you can also consider making your MedicalCondition class immutable, and I shall leave that as an exercise for the healthy reader...

\$\endgroup\$
5
  • \$\begingroup\$ Thaks you , But I would like to know more about the reason for making the return list immutable. Also I am not sure why MedicalCondition can be immutable. \$\endgroup\$
    – mc20
    Sep 3, 2015 at 14:04
  • 1
    \$\begingroup\$ @mc20 list: If you return the direct reference to your medical conditions, callers are free to modify it. You don't want a healthy patient to be virtually (pun unintended) riddled with fake medical conditions, and you don't want cancer patients to suddenly walk away being magically cured by emptying their list of medical conditions. Whether a patient is diagnosed with new medical conditions, or cured, should be communicated directly to the patient. \$\endgroup\$
    – h.j.k.
    Sep 3, 2015 at 14:16
  • \$\begingroup\$ @mc20 the case for immutability is related to the point above... even if you have a read-only view on a Collection, callers can still iterate through the entries and make changes to mutable objects of the Collection. Therefore, it may be feasible to do so, but if this is too arduous a task to consider for now, my recommendation at least is to let getMedicalConditions() return a read-only view of your underlying List. :) \$\endgroup\$
    – h.j.k.
    Sep 3, 2015 at 14:18
  • \$\begingroup\$ If I do not implement equals method in MedicalCondition , I will not be able to delete the required MedicalCondition from the mcList for a patient if I provide the another object with same content. ie. if I create another object with came content of mc1. In general, how do I need to implement delete? \$\endgroup\$
    – mc20
    Sep 19, 2015 at 18:14
  • \$\begingroup\$ @mc20 in your current implementation, MedicalCondition carries a Date field, so the question to ask is, whether "Diabetes since 1st September" is the same as "Diabetes since 2nd September". Perhaps what you really need here is a Map of MedicalCondition => Date mappings, so that the equals() of MedicalCondition is simpler, and you also eliminate the problem of adding two "Diabetes" conditions with different start dates. Modeling relapses may be different though... \$\endgroup\$
    – h.j.k.
    Sep 20, 2015 at 2:30
1
\$\begingroup\$

Redundant Imports

You import java.util.Date then import java.util.*, clearly the first is a redundant statement.

Readability

This:

private String name;
private String streetAdress1;
private String phoneNumber;
private String streetAdress2;
private String zipCode;

is exactly the same as this:

private String name, streetAdress1, streetAdres2, phoneNumber, zipCode;

only the second is easier to read. This is a matter of preference, however, as it seems from the comments.

Also, why do you declare zipCode if you're not going to use it ? I assume that's just a draft.

Class Design

I think you don't need addMedicalConditionsince it's just a shorthand of saying: getMedicalConditions().add(). The same applies for deleteMedicalCondition.
Note that this is the approach used by javafx.scene.layout.Pane and is thus the preferred approach.

If you still want to keep these methods, at least follow these guidelines:

  1. Prefix MedicalCondition with the function's corresponding term in List. For example, rename deleteMedicalCondition to removeMedicalCondition, that way it'll be easier to follow when you return to it later.

  2. Accept variadic arguments for convenience. In which case, add a plural s.

Also, you shouldn't use String for any kind of data.
For example:

  1. Street addresses and zip codes should be in a separate class responsible for locations.

  2. Phone number should be in a separate class as well responsible for communications, possibly incorporating additional functionality such as grouping of phone numbers in order of priority.

Also, why use setters if you are just going to accept all values into name ? In this case mark name public. See purpose of getters and setters.

Variable Naming

I suggest you improve your variable naming habits so that variables hold a representative name of what it's used for while being short enough for typing.
For example:

  1. streetAdress1 -> mainAddress
  2. streetAdress2 -> otherAddress
  3. mcList -> medicalConditions (variable names should not contain their data types)

Here's an idea of what I'm talking about:

class Patient
{
    public String name;
    private Location location;
    // Connection should be an abstract class with subclasses such as PhoneConnection, InternetConnection, etc.
    // The list should be ordered by means of priority and availableness.
    private List<Connection> connections;
    private List<MedicalCondition> conditions;

    public Patient(String name, Location location, Condition...conditions)
    {
        this.name = name;
        this.location = location;
        this.conditions =  new ArrayList<MedicalCondition>(Arrays.asList(conditions));
        this.connections =  new ArrayList<Connection>();
    }  

    public void setConnections(Connection...connections)
    {
        this.connections.addAll(Arrays.asList(connections));
    }

    public List<MedicalCondition> getMedicalConditions()
    {
        return mcList;
    }

    public String toString(){
        return name;
    }
 }

To answer your questions

  1. I think encapsulation is the only needed concept in this case.
  2. MedicalCondition should be an enum because, theoretically, it only accepts a certain set of possible values. For example, diabetes, cancer, etc. As you might need a specific set of MedicalConditions, using an enum would be highly-inefficient and probably an overkill. A class would be the optimal solution, and by doing that, we are sacrificing the reliability of an enum for the (technical and usable) convenience of a class. To cope with that, at least add validation mechanisms for the MedicalCondition constructor depending on possible use cases.
  3. No, only the important ones. For example, you can leave the Connection class to be used if asked for specifically in a method, say, setConnections.
\$\endgroup\$
10
  • 5
    \$\begingroup\$ I challenge that "the second is easier to read". I also challenge that a method named removeMedicalCondition should take a variadic argument. The corresponding method for List would be removeAll, which takes a Collection... \$\endgroup\$
    – Vogel612
    Sep 2, 2015 at 19:12
  • \$\begingroup\$ @Vogel612, I later noted that using a String for all these purposes is incorrect by design. About the method, it should be named removeMedicalCoditions. \$\endgroup\$
    – Amr Ayman
    Sep 2, 2015 at 19:14
  • \$\begingroup\$ Actually I find that advice also rather questionable, but alas.... \$\endgroup\$
    – Vogel612
    Sep 2, 2015 at 19:24
  • \$\begingroup\$ I agree: Putting all String variables in a line doesn't make them more readable, on the contrary, imho it makes a big lump of text and harder to read. Also String is completely fine for Strings. If all you want is to store and address that you want to print, putting the fields into String is totally acceptable.What method can you think of that your ZipCode class should have additionally? The whole address can be put into an own class, of course. And I would always use getters and setters for such fields, because you can add functionality later - you cannot with public variables. \$\endgroup\$ Sep 2, 2015 at 19:41
  • \$\begingroup\$ I also don't agree with enum since medical conditions tend to be very numerous and having a 10.000 line enum isn't fun at all. And of course, every condition is something specific to one individual. Maybe some criteria could be put into an enum (for example, symptoms), but the whole condition would not be a good choice for an enum. In this exact case, the fact that the date is part of the condition completely removes the possibility of an enum. \$\endgroup\$ Sep 2, 2015 at 19:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.