Working with the Interface Segregation Principle
- administration and governance
So we need to define at least one interface - packages - for each category in order that we can grant the appropriate access to different groups of users: read-only users, regular users, power users.
But there's more to Interface Segregation. This example is based on a procedure posted on a programming forum. Its purpose is to maintain medical records relating to a patient's drug treatments. The procedure has some business logic (which I've redacted) but its overall structure is defined by the split between the Verification task and the De-verification task, and flow is controlled by the value of the
procedure rx_verification (p_drh_id in number, p_patient_name in varchar2, p_verify_mode in varchar2) as new_rxh_id number; rxh_count number; rxl_count number; drh_rec drug_admin_history%rowtype; begin select * into drh_rec ....; select count(*) into rxh_count ....; if p_verify_mode = 'VERIFY' then update drug_admin_history ....; if drh_rec.pp_id <> 0 then update patient_prescription ....; end if; if rxh_count = 0 then insert into prescription_header ....; else select rxh_id into new_rxh_id ....; end if; insert into prescription_line ....; if drh_rec.threshhold > 0 insert into prescription_line ....; end if; elsif p_verify_mode = 'DEVERIFY' then update drug_admin_history ....; if drh_rec.pp_id <> 0 then update patient_prescription ....; end if; select rxl_rxh_id into new_rxh_id ....; delete prescription_line ....; delete prescription_header ....; end if; end;Does this procedure have a Single Responsibility? Hmmm. It conforms to Common Reuse - users who can verify can also de-verify. It doesn't break Common Closure, because both tasks work with the same tables. But there is a nagging doubt. It appears to be doing two things: Verification and De-verification.
So, how does this does this procedure work as an interface? There is a definite problem when it comes to calling the procedure: how do I as a developer know what value to pass to
rx_management.rx_verification (p_drh_id => 1234, p_patient_name => 'John Yaya', p_verify_mode => ???);The only way to know is to inspect the source code of the procedure. That breaks the Information Hiding principle, and it might not be viable (if the procedure is owned by a different schema). Clearly the interface could benefit from a redesign. One approach would be to declare constants for the acceptable values; while we're at it, why not define a PL/SQL subtype for verification mode and tweak the procedure's signature to make it clear that's what's expected:
create or replace package rx_management is subtype verification_mode_subt is varchar2(10); c_verify constant verification_mode_subt := 'VERIFY'; c_deverify constant verification_mode_subt := 'DEVERIFY'; procedure rx_verification (p_drh_id in number, p_patient_name in varchar2, p_verify_mode in verification_mode_subt); end rx_management;Nevertheless it is still possible for a caller program to pass a wrong value:
rx_management.rx_verification (p_drh_id => 1234, p_patient_name => 'John Yaya', p_verify_mode => 'Verify');What happens then? Literally nothing. The value drops through the control structure without satisfying any condition. It's an unsatisfactory outcome. We could change the implementation of
rx_verification()to validate the parameter value and raise and exception. Or we could add an
ELSEbranch and raise an exception. But those are runtime exceptions. It would be better to mistake-proof the interface so that it is not possible to pass an invalid value in the first place.
Which leads us to to a Segregated Interface :
create or replace package rx_management is procedure rx_verification (p_drh_id in number, p_patient_name in varchar2); procedure rx_deverification (p_drh_id in number); end rx_management;Suddenly it becomes clear that the original procedure was poorly named (I call
rx_verification()to issue an RX de-verification?!) We have two procedures but their usage is now straightforward and the signatures are cleaner (the
p_patient_nameis only used in the Verification branch so there's no need to pass it when issuing a De-verification).