Methods in Object-Oriented Design

2019-03-16 07:12发布

问题:

Q1. In my university studies of object-oriented modelling and design they recommend thinking about what an object can do for its method, and what its responsibilities are for its attributes. All attempts at clarification have resulted in further confusion.

This tends to generate a class diagram with actors who have all the actions, and inner classes which only hold data.

This doesn't seem correct. Is there another way of thinking about how to model the objects?

Q2. Also, the course seems to emphasize modelling the objects after their real-world counterparts but it doesn't necessarily make sense in the domain model. IE. In a medical practice, they have Patient: CreateAppointment(), CancelAppointment() but that is not how it would be implemented (you would modify a the appointment collection instead). Is there another way of thinking about this?

Example Q1

Secretary: RecordAppointment(), RecordAppointmentCancellation()

Appointment: time, date,... (no methods)

Example Q2

Doctor: SeePatient()

While SeePatient is a use-case, it does not make sense for a method on the actual class. How do you think about this?

回答1:

Unfortunately, the roadblock you've hit is all too typical in academia. Academic projects tend to start with video rental stores, libraries or student registration systems (yours is a variance of this with a doctor's office) and then inheritance is taught with animals. The guideline you've provided is also very typical

they recommend thinking about what an object can do for its method, and what its responsibilities are for its attributes

In fact when beginners ask I usually explain an object's property's are the things it knows about itself and its methods are the things it knows how to do. Which is really just another way of saying exactly what you have there. As you've discovered this way of thinking quickly breaks down when you start discussing more tangible systems and not just examples.

For instance the guideline works pretty well with this object:

public class Tree
{
    public int Height { get; set; }
    public void Grow(int byHowMuch)
    {
        Height += byHowMuch;
    }
}

While this certainly fits the bill your right to think that it doesn't "feel" right:

public class Secretary
{
    public void MakeAppoinment(Patient patient)
    {
        //make the appointment
    }
}

So what's the solution? It's a matter of taking what you are being taught and applying it. Learning and understanding design patterns will help a lot with developing systems which are more functional than a tree that knows how to grow.

Recommended reading:

  • Design Patterns: Elements of Reusable Object-Oriented Software (also known as the Gang of Four or GoF)
  • Head First Design Patterns
  • Head First Object-Oriented Analysis and Design

To solve the issue you're been presented I would probably use a combination of inherited person classes and interfaces, which would perform their actions through a series of service classes. Essentially a secretary, doctor, and patient would all inherit from person and each of these classes could be passed to accompanying service classes. The service classes may or may not do things like SeePatient(). Please don't take this example to mean that person classes wouldn't have methods.

Stack Overflow has more than a few related questions which may be of use:

  • Is Single Responsibility Principle a rule of OOP?
  • Are there any rules for OOP?
  • why is OOP hard for me?

Additionally, it would be good to check out:

  • Single responsibility principle
  • Don't repeat yourself
  • PrinciplesOfOod

Finally, there isn't a single definition of what makes an application object oriented. How you apply patterns, principles etc. will define your program. The fact that you are asking yourself these questions shows that you are on the right track.



回答2:

Q1 Is it possible responsibilities of your objects should be interpreted as authorization or contract requirements, as in what actions they should take? So, to take a medical example from your Q2, an object with a Scheduler role (think C#/Java interface, or Obj-C protocol) has attributes around CanEditAppointments or EditsAppointments.

Q2 From a use case perspective, a patient may be able to create an appointment, and so you might implement a Patient in your object model with a method to CreateAppointment(). But, in terms of encapsulation, you would likely instantiate an Appointment object in CreateAppointment(), and then call methods or set properties on the Appointment object to set its time, date, patient, physician, etc.

And because the Appointment collection is likely to be permanent storage like a database, it would likely be the Appointment object's responsibility to add itself to the collection (Appointment.Schedule() goes through your data access layer to save itself to the database).

That also ties back to your Q1, in that the Appointment object's responsibility is to save itself, so it might implement an ISaveAppointment interface that requires fields and methods to carry it out. It also is the Appointment's responsibility to have a date, and time, and patient, etc., before being saved, and so the ISaveAppointment interface should require they exist, and Appointment.Schedule() should validate the values are correct or have been previously validated.



回答3:

You are right that in many cases there are higher order things which more naturally contain behaviour, like the system, or the user.

You can model this behaviour in classes as static methods which operate on the data model. It isn't OO, but it's fine. You can group related methods together into such classes, and soon you have the notion of "services", as in service oriented programming.

In Java there are specifications and standards for creating such classes, namely the stateless session bean in EJB. The Spring Framework has similar notions with the stereotype "Service" which can be applied to classes to tag them as being facades for business logic.

A service is then a component which encapsulates a certain functionality or behaviour in the system. It operates on a given object model (either its own internal one, or the more general business object model from the system). If you take your use cases and create services which relate directly to them, you can write very maintainable software.

The DCI Architecture is a formalisation of this and an attempt to do the same, but at the same time trying to stay true to object orientation, by adding behaviour to objects as they need it.



回答4:

I still experience the confusion: "am I telling you to do something else" or "am I doing something someone else asked of me"?

Perhaps all you have to do is play Barbie's, or G.I. Joe's to understand object interaction and where responsibilities go. G.I. Joe got wounded (or Barbie broke a nail) so he calls the Dr's office. "Hey doc, this is Joe, I need an appointment." So you, the kid, tell Barbie to go to the doctor, who needs to know the doc to call and how to call - a reference and public MakeAppointment() method. The Dr. office needs to put the appointment on the books - it's own BookAppointment() method that is the office's procedure for handling appointment requests.

public abstract GenderAppropriateDolly {
     protected DrOffice  Drkilldare;
     public override MakeAppointment() {throw new NotImplementedException();}
}


public class GIJoe : GenderAppropriateDolly {
    DrKilldare = new DrOffice();
    List<Appointment> myAppointments = new List<Appointment>;

    public void MakeAppointment () {
        myAppointments.Add(DrKilldare.BookAppointment(this));
    }
}

public class DrOffice {
    List<Appointment> officeAppointments = new List<Appointments>;

    public Appointment BookAppointment(GenderAppropriateDolly forWhom) {
       Appointment newappt = new Appointment(formWhom);
       this.Appointments.Add(newappt);

       return newappt;
    }    
}

public class Kid {
    GenderAppropriateDolly myRoleModel = new GIJoe();

    // Joe got wounded so ...
    myRoleModel.MakeAppointment();
}