nursing information system reengineering a mobile nursing information application prepared by larry...

29
Nursing Information System Reengineering a Mobile Nursing Information Application Prepared by Larry LeFever For Software Engineering Seminar II Pace University White Plains, NY Spring 2003

Post on 21-Dec-2015

216 views

Category:

Documents


2 download

TRANSCRIPT

Nursing Information System

Reengineering a Mobile Nursing Information Application

Prepared by Larry LeFeverFor

Software Engineering Seminar IIPace UniversityWhite Plains, NY

Spring 2003

- Technology employed: J2ME/MIDP

- Targeted Device: Palm Pilot

- Primary Purpose: Nursing students make note of physical assessments of patients on the mobile device, then save them to the desktop, where professors may review them

- Prior versions developed by other development teams

- Objective: current team (just myself) to enhance the project to enable multiple patients’ data to be managed on the device at once; and hopefully fix performance-problems

- Results: new use-cases implemented:- New Patient- List All Patients- Select Patient By ID- Delete Patient By ID

- Issues: Lack of “Pattern-Awareness” causing:- robustness problems (memory-errors)- UI-performance problems- scalability problems- code-maintenance problems- “code-rot”

- Recommendations:- radical re-design, applying certain GoF Design Patterns

Unfortunately, the NIS project is an excellent example of the adverse consequences of a lack of “pattern-awareness” in software-design.

There has been anticipation of the project soon nearing completion and being ready for release and possible marketing.

However, the v1.0 codebase lacks the design-sophistication required for it to be production-ready.

The “bugs” in the latest version are attributable to design-errors more than to implementation-errors.

So, the central theme of this presentation is not:

- developing software for mobile devices

Or

- developing software for the healthcare field

DesignPatterns

BestPractices

What is a “Design Pattern”?

“Christopher Alexander says, ‘Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.’[AIS+77, page x]” [GOF;2]

Designing for Change:

“The key to maximizing reuse lies in anticipating new requirements and changes to existing requirements … unanticipated changes are invariably expensive … Design patterns help you avoid this by ensuring that a system can change in specific ways.” [GOF; 23-24]

Loose Coupling:

“Each design pattern lets some aspect of system structure vary independently of other aspects, thereby making a system more robust to a particular kind of change.” [GOF; 24]

Toolkits vs. Frameworks:

Toolkit: - code-reuse - OO subroutine-library

Framework: - design-reuse - “captures design-decisions … common to … application domain” [GOF;27]

A software-design maximof my own:

For all but the simplest software projects (such as single-class single source-file ad-hoc utilities), write every application as a reference-implementation of a new framework.

Later, others will have the option of using only the framework or substituting their own components for some of those of your reference implementation.

Anti-Patterns:

Recurring design-errors, expressed abstractly, much as are design-patterns; typically associated with one or more design patterns cited as recommended fixes for the errors in question.

Main NIS v1.0 Anti-Pattern:

“The Leak Collection” [TATE; sec 6.4]:

“Whenever we have a collection with a long life cycle that has the potential to contain objects that aren’t removed, we have the possibility of a memory leak.”

“Memory Leaks” in Java?

Yes. Whereas in, e.g., C++ a memory leak occurs when one loses one’s last reference to an allocation, in Java, memory leaks occur when a non-obvious reference is accidently retained.

Relevant “Leak Collection” Scenario:

“User interfaces: Most modern user interfaces are collections of windows and components. Many times, these potentially massive collections have persistent anchors … Whenever a user interface is attached to an anchor with a long life cycle, (Java-style memory-leaks are possible).” [TATE; sec 6.4]

Relevant Anti-Pattern “Force”:

“References are in clusters, so the impact of leaks is greater. … If you forget to remove a critical reference to a single object, it may reference many other objects. A Java interface developed with the Swing component library can have references to dozens or even hundreds of child classes. Further, since the classes also have back pointers, references to a child class can make an entire user interface tree reachable.” [TATE; sec 6.2.2]

http://developer.java.sun.com/developer/Books/javaprogramming/bitterjava/bitterjavach06.pdf

… the GUI presents only the information that the user requests to see; the BOOP layer only keeps in memory the relationship and structure logic of the system currently in view; and the DataStorage layer preserves all other information on file, writing systems that are coming “out of view” to file, and reading and building the new systems as they are requested to come “into view.” The dynamic presentation ability allows the NIS to exist on a small footprint device. The dynamic business and datastorage ability allows the program to create a physical assessment of unlimited size and variation – so the program can evolve and adapt to the user’s needs. [NIS1;pg.1.3]

@files = glob("*.java");foreach $file (@files) { open(INFILE, $file) || die "could not open " . $file . "\n"; open(OUTFILE, ">out/$file") || die "could not open out-file " . $outDir . "\\" . $file . "\n"; print STDOUT "******* processing " . $file . " ********\n\n"; $currClass = "NoneFound!"; while(<INFILE>) { if(/class\s\s*(\w\w*)/) { $currClass = $1; print OUTFILE; next; } # (... so, it's not a class-decl line...)if(/\s*(public|private|protected|static|synchronized|final){1,3}\s+\w+\[?\]?\s+(\w+)\s*\([^\)]*\)\s*{/ || /(\s*)(${currClass})[^{]*{/) { $currMeth = $2; $_ .= "if(NISApplication.DEBUG) System.out.println(\"in " . $currClass . "\." . $currMeth . "\");\n"; print OUTFILE; next; }…

in BodyTouch.BodyTouchin BodyTouch.initializeDatain PatientInfo.PatientInfoin NTextField.NTextFieldin NTextBox.NTextBox...in NTextBox.NTextBoxin Body.Bodyin BodyBuilder.BodyBuilderin PatientInfo.PatientInfoin NTextField.NTextField...in AnatomySystem.setAssessmentin AnatomySystem.setPhysicalExamin AnatomySystem.setTreatmentPlanin Body.addASFinished the building=======================================================

lefever@COMPUTER ~/emulator-win$ egrep -c '([A-Za-z][A-Za-z]*)\.\1' STDOUT_2.txt

377 <==== wow! 377 ctor-calls on start-up!

2. Reusing and recycling objects to avoid the memory expense of new object creation. For example, instead of creating 15 List screens, one for each anatomy system (head, neck, etc.), only one instance of the GList class iscreated and it is redrawn and rewritten as needed. [NIS1; pg.1.9]

3. Releasing resources once they are used. This includes setting objects to null and calling the system garbage collector (System.gc()) when necessary, allowing memory to be freed up for new operations. [NIS1; pg.1.9]

private class DataTimer extends TimerTask { NISApplication nisa; Display d; public DataTimer(NISApplication nisa, Display dis){ d = dis; this.nisa = nisa; } public final void run(){ BodyTouch BT = new BodyTouch(nisa, d); BT.initializeData(); BT.setDisplay(d); d.setCurrent(BT); // !!!! Object-model “anchored” in display !!!! //finally, cancel this timer task? cancel(); } }//end DataTimer

From: NISApplication.java

public void generateList(String ASname){

if(NISApplication.DEBUG) System.out.println("in BodyTouch.generateList"); bbuilt = BB.returnBody(); // Getting already loaded “object-model”!! GL = new GList(display); // repeatedly instantiating UI-object !! try { AnatomySystem as = bbuilt.getAnatomySystem(ASname);

//***STEN ADDED CHANGE HERE-CALL TO // buildGList takes String!!

GL.buildGList(as, ASname); }//END try catch (Exception ignored){}//END catch

backtoBody = new Command("Body", Command.BACK, 0); GL.addCommand(backtoBody);

GL.setCommandListener(this);

}//END generateList

From: BodyTouch.java

AxillaeBreastsReviewofSystems.addCondition(DoesBSERegularly); AxillaeBreastsReviewofSystems.addCondition(WantsBSEInformation);… AxillaeBreastsReviewofSystems.addNTextField(LastMammogram); … AnatomySystem ABAS = new AnatomySystem("Breast/Axillae",AXILLAEBREASTS); ABAS.setReviewofSystems(AxillaeBreastsReviewofSystems);… ABAS.setTreatmentPlan(AxillaeBreastsTreatment); B.addAS(ABAS);

//voiding the objects - hopefully to save some space DoesBSERegularly = null; // ineffective! Not setting ref-counts to zero WantsBSEInformation = null; // since, per preceding lines, objects have been Tenderness = null; // passed to other objects held long-term BreastSurgery = null; // in the “object-model”, anchored in the display NippleDischarge = null; SkinLesion = null; SurgicalScars = null;… return B;

From: BodyBuilder.java

Principal Recommended Design Patterns:

- Flyweight: - reuse, in most cases, one of each relevant type of widget, prepping each with relevant data from a domain-object (in some cases, one needs to maintain a pool of a given widget-type, to be able to display multiple at once)

- Visitor: - implement the RecordWriter as a Visitor - keep track of “dirty” domain-objects - have RecordWriter pass itself to each “dirty” domain-object, to have latter persist itself.

GUI AppStateMgr DataMgr RecordRdrBodyBuilder

NIS v3.0 ProposedApplication-Initialization

new GUI(display)

init buildMainMenu

showMainMenu

init init

showBusyBar

buildPatIDRecIDMap

* readRec()

* rec.getPatID

getPatIDs(etc.)

rec = new PatRec()

could cachemost recent nrecs

could showprogress-bar,but that requiressome kind ofsignalling fromobjects beinginitted; whereascurrent versionshows falseprogress, sinceit's not tied toactualinitializations

body doesn'tneed to be builtyet; need to initlist of PatIDsthough

MIdlet

extendsByteArrayInputStream

each PatRec isassociated with itsown "RecordStore"-file

GUI AppStateMgr DataMgr RecordRdrBodyBuilder

NIS v3.0 ProposedBody-Loading

MIdlet

selectpatient by

ID

setPatientID

e.g., up to last 3records hereincached

getBodyMap

showBodyMap

(show head-menu)

[_headMenu == null] _headMenu := new ListMenuelse _headMenu.reset()

prepListMenu(_headMenu, DataMgr.HEAD_SYS)

getDomainObj(DataMgr.HEAD_SYS, patID)

[!domainObjFound] getRec(DataMgr.HEAD_SYS, patID)

[recs[patID] == null] [recs.length==MAX] recs[MIN_ID] := null readRec(patID)

preventsexceeding of MAXrecs in-memory

re-use the widget(as "Flyweight")

headMenu.setData(domainObj)

showMenu(_headMenu)

field :=readField(DataMgr.HEAD_SYS)

createDomainObj(field)

<<Flyweight>>Widgt

<<Persistable>>DomainObj DataMgr RecordWriterBodyBuilder DefaultRecordWriterImpl

NIS v3.0 ProposedDomain-Object Persistence Process

GUI

"new patient"OR

"selectanother by ID"

OR"Exit"

getDomainObj().setDirty()

writeRec()

widget edited(keyPressed

oritemSelected)

setDirty(this)

dirtyMap.put(dObj.getID(), dObj)

hasDirty()

currRec hasbeen selected,via prior "newpatient" or"select patientby ID"

"Flyweight"cannot assumeits "extrinsicstate" hasn'tchanged sincelast setDirty-call

[!dMgr.hasDirty()] return;

nothing toupdate, soreturn early;so, no record-writing if use-case is read-only

"Template Method" Pattern:

final void doWriteRec() { while(byteArray.offset < byteArray.length) { fieldID = readFieldID(); Persistable aPersistable = dMgr.getDirty(fieldID); if(aPersistable != null) { // delegate to Visitable aPersistable.persist(this); // Visitor-"accept" } else { // write it w/o Visitable-intervention writeFieldRaw(); } }}

[dMgr.hasDirty()] [!recLoaded(currRecID)] [recCache.isFull()] // prevent cache-overflow recCache.dropItem() readRecRaw(currRecID) doWriteRec()

persist(wtr:RecordWriter)

writeString(s:String)

writeStringRaw(s)writeOptionSelection(optionIdx)

writeOptionSelectionRaw(optionIdx)

"Composite" Pattern:

[hasChildren()] foreach child in children { child.persist(wtr); }

/*if instanceof AnatomySystem && hasParent() { // is "AnatomySubsystem" // (no "AnatomySubsystem" class)}*/

implements "raw"-methods, per NISfile-format

Thank you