Category: OOP

Protocol-Oriented Programming: Overcoming Inheritance Woes

When object-oriented programming appeared it was touted as a better way of writing software. Inheritance, encapsulation, and polymorphism were the three pillars of OOP that would change the way software was designed and developed. Like all new ideas, some things work out better than others.   In real-world experience, polymorphism and encapsulation worked as expected. Inheritance, on the other hand, often proved to be the proverbial rope with which one hangs oneself.

Inheritance is one of those things that it is easy to get carried away with. When someone is new to OOP, the world begins to look like a large inverted tree–hierarchies are everywhere.  Unfortunately, too much reliance on inheritance creates headaches.

Consider this scenario…

Many different people may use an EHR system. Aside from allowing for logins, there are many good reasons for making clear distinctions between users.   Consequently, it makes sense to create multiple roles that may be assigned to users.   The only limiting factor to the total number of roles (and any differences between them) is the imagination of the designer and the ultimate requirements of the systems.

Person Hier

In the initial design phase, there are three main EHR groups: clinical, administrative, and patient. Since all of these refer to humans, it is natural to see a hierarchy with a generic person at the top and these three groups one level down.

class Person {
var lastName: String
var firstName: String
var DOB: String
var gender: String

}

When constructing an inheritance hierarchy, one has to choose properties and behaviors for the base class (Person) that make sense when creating classes further down the tree.   That is, if the base class has too few properties or methods, then there is little reuse in subclasses, requiring that one write possibly redundant code for subclasses. However, err on the side of fat base classes, and subclasses have a lot of overridden or ignored members. In my experience (and I am not claiming to be the world’s greatest OO programmer), unless the inheritance tree is shallow (say three levels or fewer) there is a constant revisiting of the base class to make way for newly discovered needs in subclasses.

Yeah, I know a thorough analysis should be done before creating classes, but in the real world where someone is paying for a custom program, they decide when features are added, not the designer. The fact is, in real life, there is only so much one can do to freeze the project at any given stage – things change. Now, if you have a great idea for an app that you will build without outside input, then freezing is much easier (though feature creep will still occur).

The Person base class looks fine to start.   The first challenge occurs when one gets to separating out clinicians from administrators and patients. Each has different properties and different behaviors.   For example, clinicians will have access to clinical decision support functions, and administrative folks will not. HIM managers will have access to reporting functions that others will not.

 

There may be many different subtypes for each group–not all clinicians are employees, some administrative personnel are salaried, patients may be surgical, medical, or OB.   At times, it might become necessary to pass an object to a routine or add it to a collection. When that happens, if the objects are not the same type, then only the properties and methods inherited from a common higher-level class will be available to the collection or routine.

var myArray: Array <Person>

This array will accept any class derived from Person, but will not have access to properties from subclasses. So getting everyone together is possible, but has significant limitations.

Consider a case for which the goal is assembling care teams in the EHR.   A care team consists of members from all three classes-clinical, administrative and the patients they have in common.   For each member of the team, we want access to all of his/her care-related properties and methods.

var careTeam: Array<Person>

As we know, this array will not do. All we would have access to would be first and last names, DOB and genders of the team members.   We need a way to add all three groups to a care team without creating a specific care team class (which is another possible approach). Protocols provide a perfect solution.

Protocols
Protocols (called interfaces in Java and PHP) are chunks of code that contain signatures ( outlines or skeletons) for properties and methods. What makes them useful is that they can be added to classes to provide functionality on an ad-hoc basis. Here is a protocol that defines properties and behaviors for a care team member.

protocol CareTeamMember   {
var teamRole: String {get set}
var joinDate: String { get set}

func setEncounter ()
func listEncounter()

}

The CareTeamMember protocol allows one to set a job role (primary care doctor, physical therapist, etc.) and the date the member joined the team.   The two methods allow the team member to handle appointments (setEncounter) or display an appointment calender. Now that we have this protocol it can be added to a class.

If we add it to Person is would appear as:

class Person: CareTeamMember {
var lastName: String
var firstName: String
var DOB: String
var gender: String

}

However, now that have a protocol, we can create teams without worrying about class differences.

Let’s create the next level of classes.

class Clinician: Person, CareTeamMember {
var Specialty: String
var Department: String

}

class Administrator: Person, CareTeamMember {
var Dept: String
var jobTitle: String

}

class Patient: Person, CareTeamMember {
var medRecNum
var diagnosisList: Array <String>

}

Each class conforms to the CareTeamMember protocol and has those functions and properties included. Now we can create the array to handle the team!

var careTeam: Array<CareTeamMember>

Since protocols do not contain implementation code, the method bodies can be adjusted for each class. Thus, in the patient class, setEncounter requests an appointment by sending notices to clinicians and administrators. For clinicians, it gives the approval to add a patient to the clinician’s appointment calendar. For administrators, it makes the appointment official.

In Swift 2.0, protocols became more powerful with extensions. Extensions allow one to add complete methods (not just outlines or skeletons) to protocols or any type in Swift.   For the care team example, it means that one could write code for the listEncounter method that displays an appointment calendar by reading the teamRole variable.   That code would be used by every class that implements the CareTeamRecord protocol.

protocol CareTeamMember   {
var teamRole:String {get set}
var joinDate:String { get set}

func setEncounter ()
func listEncounter()

}

extension CareTeamMember {

func listEncounter () {
getJobRole…
displayCalendar…

   }

}

Protocols help to prevent some to the messes that inheritance can cause.   Protocols make it easy to get classes or types that are exactly what you want without having to worry about inheritance trees or object types. With protocols and extensions, base classes can be clean and the number of subclasses can be kept to a minimum. Routines that you want to reuse frequently (e.g., printing, listing, etc.) can be tacked onto classes as needed, saving a lot of typing and testing effort.

Protocols and extensions are the kinds of features that one wonders what they are good for right up until the first time hours are spent trying to get just the right base class for a large inheritance tree. Yep—been there, done that…

Facebooktwittergoogle_plusredditpinterestlinkedinmail