Enemy of the State
Enemy of the State
· What in the world is a “state machine”? An ABAP Example
· How to achieve this using a previous “domain specific language” example
· How to constantly tear apart and improve existing programs to achieve things like the above without destroying all life throughout the universe
Louis L’Amour once said “Nobody got anywhere in the world by simply being a TABLE OF CONTENTS”
· State Machines and Design Patterns in ABAP
· Ripping apart and refactoring existing programs
· Example code for a State Machine framework in ABAP
· Cliff-hanger Ending
I have two series of blogs going at the moment. One is about the project I am working on for Baron Frankenstein, in which I hope to prove the benefits of OO development based upon unit testing. The other, into which category this one falls, is a bit more esoteric / academic / abstract and so may not be of much interest to any but the most technical minded ABAP programmers. Hopefully there are some out there.
I have been going through these so called “design pattern” things, and when I find one I don’t understand at all, or which seems to have no practical application in the SAP world, I try to write a blog about it, with the aim that you can’t write about something until you have truly understood it. So I attempt to find an article on the internet with an example of such a pattern, and then write an ABAP program to do the same, and then, the bit that makes me grit my teeth, see if I can’t improve on the original example. Only then am I happy I’ve got my head around the thing. Then of course, comes the really difficult part – using it in a real life situation. Only then can I truly say it is not just a piece of academic waffle.
Thus far I have done this with the “design by contract” pattern (if it is a pattern) http://scn.sap.com/community/abap/blog/2012/09/08/design-by-contract-in-abap, and the “decorator” which had me foxed when I first read about it http://scn.sap.com/community/abap/blog/2013/03/02/oo-design-patterns–decorator–in-abap.
In this blog I am going to have a word with the “state” pattern which as we will see was a total mystery to me at first, and in the future the next cab off the rank will be the “visitor” which is another one I read assorted articles about and end up none the wiser.
51st State Pattern
Some time back, on 08/01/2013 I wrote a blog about Domain Specific Languages inspired by an article from Martin Fowler.
This is the sequel. At the time, I wanted to see if I could implement his Java program in ABAP, and I managed to write something that worked, and I hope stayed true to the points he was trying to make.
If you read the blog via the link above you can see me saying again and that I had no idea what a State Machine was.
When I read the book “The Clean Coder” by Robert Martin he said that any programmer that knew their stuff should be able to rattle off the difference between a Mealy and a Moor State Machine, and if you did not know then why not?
So I looked both up on the internet, read the Wiki definitions, and still did not understand. I was starting to wonder if I was really cut out for this sort of thing.
Then I read the “Head First Design Patterns” book and they explained in great detail about the State Pattern, using such a simple example even I could get it through my thick head. At the end of this blog I will include a text file whereby I implement the exact same example but in ABAP.
It was exactly the same thing as far as I could see – in both examples you have a machine that is waiting for user input and changes state based upon what the user does, and the reaction of the system to a user action is not just based on what the user just did but also on the system state.
As an example, I used to work in a huge great skyscraper and in the morning there would be a big bunch of people in the foyer standing just before the lifts waiting for a lift to arrive at the ground floor. There were about five lifts and every one would have a green light saying that a lift had been summoned, but because the building was so tall you often had to wait quite a bit before even one would arrive,
Naturally, every time a new person arrived in the foyer they would push past the vast amount of people already waiting, and go up to the lifts and press one of the buttons to summon a lift. The temptation to shout “Oh my god, you are a genius – I would never have thought of that!” was overwhelming but I always managed to hold myself back.
It’s the same when you are waiting at a crossing and the person beside you presses the button to say they want to cross the road every two seconds until the little green man actually starts flashing. If they were to stop doing that, say they got a mobile phone call and had to interrupt their vital button pushing activity to answer it, someone else would instantly appear to take up the slack. I was happy to see mid 2013 in Moscow that you actually get a countdown so you can look at on the other side of the road as to see how many seconds until when you will be able to cross. I was even happier to see one in Brisbane for the first time last month. I don’t suppose that will stop people pressing the button repeatedly.
How this relates to state machines is that I suspect the following:- that once a lift summoning button has been pressed once, the system state changes from “idle” to “lift has been summoned” and no matter how many times you press the button again I don’t think it makes any difference. At some traffic light junctions the buttons are purely cosmetic to give people something to do while they wait for the lights to change, but in that example the system state never changes, so that’s not pertinent to this example.
You might now expect me to talk about writing an OO lift simulation program, but that would be too obvious. I want to see if I can use the existing state machine program I wrote per the Martin Fowler example (called “Gothic Security”), copy it making minimal changes, and the result will be an ABAP version of the Java state machine program in the Head First Design Patterns book, which is a “Gumball Machine” by the way.
As an aside, I ask the world : is there an example of the state pattern anywhere in standard SAP? Has anyone out there written a custom program using this pattern?
The Abstracts of My Tears
That Martin Fowler program went out of its way to abstract out the technical processing from the business rule definition. The promise of OO programming is that if you depend upon abstractions, then those abstractions can be re-usable. I have no doubt you can achieve the same with procedural programming but the idea – so I am constantly told in books – is that code re-use is easier with OO.
At first glance it seems the “Gothic Security” program has a large chunk which could be turned into some re-usable Z classes, and used again and again whenever we want to use the state pattern.
The funny thing is design patterns aren’t really supposed to have their code re-used – the idea is “you can use this solution a million times over, without ever doing it the same way twice” — Christopher Alexander, a pattern is supposed to be a guide to how to do solve a problem as opposed to a fixed re-usable template.
In the book “refactoring to patterns” by Joshua Kerievsky the quote is “true value of this book lies not in the actual steps to achieve a particular pattern but in understanding the thought processes that lead to those steps”.
Nonetheless the opposite argument is that the only reason design patterns is exist is because of gaps in the programming language. In the wiki entry on design patterns in the “criticism” section you can see several people who have made a huge effort to introduce language support for the major design patterns.
If I were to take ABAP as an example, it is clear that several design patterns have been “baked into” the ABAP language:-
· Observer Pattern – in the examples (Java) I have seen you seem to have to go to quite an effort to set up the observer pattern, but ABAP has the SET HANDLER FOR command to let classes subscribe to events of other classes, so the language seems to do the hard yards for you
· Model View Controller – SAP has always made a big thing about this, and the pattern is in fact mandatory in Web Dynpro
· Prototype – this is all about cloning an instance of an object. In SAP this seems to be embedded at the kernel level a la SYSTEM-CALL OBJMGR CLONE me TO result.
· Proxy – I was reading the Head First Design Patterns chapter on the proxy patterns and they were talking about the incredibly complicated Java things you need to do to get remote proxies working so remote systems can talk to each other, and my eyes glazed over, and I thought this has no relevance to me, and then the light bulb went on and I thought “hang on – I did the same thing in SAP this very day”. That is what transaction SPROXY (which lets SAP talk to PI and vice versa) is all about, built in support for a complicated design pattern.
This week we’re in a car park, but the rules of the game remain the same
The rules of the game are very simple – I needed to create a program in ABAP which does everything the “Gumball” program from Head First Design Patterns does, using the state pattern. Hang on, I hear you cry, you have not yet mentioned what those requirements are!
Well I am not going to. I am going to show you the code instead. One of the key elements of the whole test driven development / unit testing approach is that you should be able to grasp the requirements (what the program should do) by looking at the tests. How the program does this is something totally unrelated, and not of any interest to many business types.
Regardless of whether I was a procedural programmer or an OO one, if I had to write a Gumball Machine program, I would copy my previous Gothic security program, because it did almost exactly sort of the same thing, and then start changing it.
The proof of the pudding is – do you have to change LESS in an OO program?
I haven’t done any of this yet as of the time of writing, so I don’t know the result. I have years of experience of changing procedural programs so I know how much effort that involves. It could be that the OO version is so much easier to change it will prove the OO vs. procedural debate so conclusively I will never even wonder about it again, and use this as an example to convert all my colleagues.
It could be that it makes no difference at all. Either way, I am burning to know.
I now take a copy of my OO Gothic Security program, and begin to convert it to an OO Gumball Machine program. To start off with this seems like just a series of find and replace operations.
In both cases we have the business rules written down in the form of circles with arrows pointing to each other. Each circle is a possible state of the system. This is like an old children’s TV show in the UK called “runaround” where the host would shout “runaround now” and the children would dash madly from one circle to another.
I am merrily doing my find and replace, when suddenly I am horrified to find that my whole scientific experiment has been rendered pointless by me running headlong into one of the core ways my mind works, and that is – I find it impossible to return to a program I wrote in the past, even a short while in the past, without thinking “that’s rubbish” and totally re-writing it.
Re-Write My Fire
There are two schools of thought here. I imagine the most common is the “change as little as possible to reduce your chances of breaking something” attitude, on the reasonable grounds that breaking things can get you sacked. The exact opposite opinion is mentioned in the Foreword to “Clean Code” where Fred Brooks http://en.wikipedia.org/wiki/Fred_Brooks is quoted as saying all major chunks of software should be rewritten from scratch every seven years. Then the foreword postulates that should be decreased to every seven hours. That’s going a bit far, even for me.
The Code Cleaner
I constantly mention in my blogs the “Boy Scout Rule” from Robert C. Martin about leaving code cleaner than when you found it. As an example, this very day I found the following in a program I had to fix.
…. Loads of commented out code ….
LOOP AT IT_SOMETHING.
…. Loads of code ….
… then a comment saying the above code did not seem to be working, and a helpdesk reference, from 2001 …
FROM VBAP INTO TABLE IT_SOMETHING.
LOOP AT IT_SOMETHING.
… the exact same longs line of code as in the loop above ….
As might be imagined, when debugging this, the second loop worked a lot better than the first. I can only presume that whoever tried to fix this12 years ago did not want to touch the existing code, for fear of breaking something, so wrote some extra code below it to get the data from the database, then did a big copy and paste and everything worked, problem over.
However, I wouldn’t let it lie. I could have let it lie, but I didn’t let it lie. I should have let it lie, but I wouldn’t let it lie. I deleted all the commented out code, and the loop over the empty table. I might have broken something. I didn’t, but I might have. Some people would say I was evil, EVIL I TELL YOU!
My crimes don’t end there. It gets worse. Sometimes I even change the technology, like replacing WRITE statements with the ALV.
I can imagine people reading the above and thinking – “hang on, you are changing not only what you are supposed to be changing, but bits of the surrounding code as well? That’s a waste of time, and more importantly money, and even more importantly introducing an unacceptable level of risk.”
On the face of it, that’s a valid point. The major custom programs at my company, like at many others have evolved over a long time, and just like an ancient Roman ruin, the beauty of the original has been obscured by layers and layers of mud over time, till nothing is visible of what it was supposed to do originally.
They are now so fragile that if you look at them sideways they stop working, and yet the constant stream of change requests never stops. If you fix one part, two seemingly unrelated parts break – but people are willing to tolerate that – it’s normal. Generally programmers would not be allowed to try and make the thing less fragile, on the grounds that it is fragile. Eventually it is deemed too dangerous to make any change at all.
Have a look at this blog:- http://scn.sap.com/community/abap/blog/2013/12/01/antifragile-software
The important point there is that the book on things that were “anti-fragile” was nothing to do with software. It was about political or biological systems which responded to change and stress by getting stronger rather than falling to pieces.
What Doesn’t Kill You Makes You Stronger
We want to reverse this – we want good old Aunty Fragile to turn the inevitable torrent of change from a disaster in waiting to a Good Thing – we want each change to make each subsequent change easier, which is the opposite of the normal order of things. Each change should make it more obvious what is going on, should reduce the complexity rather than just adding endless extra chunks of conditional logic, or should increase the separation of concerns so a change in one place is less likely to break something far away. This is the “Boy Scout Rule” once again. Now, you may say I’m a dreamer – but I’m not the only one. One day you will join us, and the ABAP world will be as one.
The obvious thing to do is to slowly but surely have unit tests for all your code. Then you know for sure when a change to one part breaks something else. That’s not an easy task; usually you have to break your program into bits e.g. extracting the database access statements into a separate class. Also code with proper unit tests is twice as long as “normal” code, and so takes twice as long to write. Also if you start making changes to enable unit tests chances are you will break something. Also there is no obvious (instant) business benefit.
It’s not looking good, is it? I imagine very few people would get permission to make the required changes to existing programs. I work for a fairly enlightened company, so it’s all right for me, though I am moving as slowly as I can. Maybe the best people can hope for is to be allowed to write unit tests for new developments, though when anyone hears you have to write twice as much code, that idea may also go straight out the window as well.
I have become convinced the benefits are so great that any amount of effort is justified, but I may be crying alone in the wilderness here. Now it is time for a shameless plug.
I am giving a talk on this very subject – how unit tests increase the stability of programs – at the Mastering SAP Technology conference in Melbourne which runs from 29/03/2014 to 02/04/2014 (the conference, not my talk).
Is this why I have gone off the topic of state machines and started waffling on about unit tests? Is it Penry, the mild mannered janitor? Could be…..
Let’s presume the can is already open
OK, now you have added unit tests to your program, and have 100% test coverage. Well done! Now you can change anything you want with impunity, and be sure nothing breaks. Here are examples of some of the “boy scout” changes I have been making recently…
Clarity : I came across a chunk of code which appeared all throughout a big complex program, subtly different each time. Firstly I moved it into its own subroutine, so the behaviour became consistent, and future changes would be in one place only. This was popping up a box to get the user to enter some data, in four different circumstances. The problem was each conditional test used obscurely named variables, so I had no idea what was actually intended. I needed to know to see which, if any, of the four slightly different blocks of seemingly identical code was correct. Luckily for me, sitting opposite me at that exact instant was a guy who had been with the company for forty years. Not everybody has that luxury. Anyway, it was all obvious to him, he rattled off the four occasions when that box should pop up and why. I added this as comments at the start of the subroutine i.e. WHY this was happening, and what was going on in the real world outside of SAP that made this behaviour desirable in certain circumstances. Armed with this knowledge I could know see which of the four blocks of code was correct – none of them. They had all started the same with one condition only, and over the years the other three conditions had been added to one or more of the blocks of code, whichever ones the programmer came across, always missing at least one.
Consistency : In this program were about fifty different places where pop ups were shown to the user, sometimes three or four in a row. They were mostly called with CALL SCREEN STARTING AT X Y mixed in with a few POPUP_TO_CONFIRMS. As an academic exercise I looked to see how many of the CALL SCREENS started at the same co-ordinates. It was none of them. So if a user went through four pop ups, the boxes would be all over the screen like a mad woman’s breakfast. One of my colleagues suggested this was intentional, to force the user to move their eyes around the screen so they would pay attention to the contents of the box. I suspected the truth was every pop up box had been coded by a different person over a ten year period and they each just picked a start position at random. In the same way, the code inspector warns you when a function key other than F12 is used for “cancel”. In the program I was looking at, about fifteen different function keys on different screens pointed at cancel, but F12 was nowhere to be seen.
Clarity (variable naming) : There was a very strange piece of code which exported some data to a Z table, and then SUBMITTED another Z program which read back that exact same data from the Z table and printed it out. That did not seem very sensible to me, so I called a function module instead; passing it the data in an IMPORT parameter, and the function module had the same code as the Z program. That broke things. It took a lot of looking but I discovered the culprit was a control variable called L_CNT. I had presumed that because it started with L it was a local variable, and indeed it was declared at the start of a subroutine, but on close inspection it was declared thus:-
So, it was a global variable really. That was fine when the code was called via SUBMIT, it was always initial, but that did not work so well during repeated calls to a function module. Upon even further investigation, the variable was declared as a STATIC, and named as it was, because the whole section of code had been cut and pasted from the internet as example code of how to print a SMARTFORM with multiple copies in the same spool.
Clarity (Variable Naming) : Whilst debugging a problem I found a variable with a name like COMBINED_WEIGHT which gave me a false idea of what it might represent. After a lot of searching I found that this global variable was only filled if a user had been given a pop up and manually entered the weight. So I changed the variable name to MANUALLY_ENTERED_WEIGHT. After a good old FIND/REPLACE the code made a lot more sense in certain places, and made no sense at all in others, and then I zeroed in on the bits where it made no sense and before long my problem was solved. This is following a principle I read somewhere on the internet called “make bad code look bad”
Clarity (Comments) : This next one I found two weeks ago and it made my bang my head against the wall. I had written a comment above a line of code as a warning. The context is, this code is inside an implementation of a standard SAP BADI.
“I have discovered if you call SET_DATA more than one time in this user
“exit then you lose some of the changes e.g. blanking out the quantity
IF lf_change_needed = abap_true.
im_item->set_data( ls_item ).
I thought that was a useful warning – again it did not say WHAT the code was doing, but highlighted a problem to be avoided.
Then the next programmer came along a few months later and needed to insert a big chunk of code. He decided the best place for the new code would be between the comment and the IF/ENDIF statement above. It was a big long section of code, so the poor old comment was moved up well away from the command it related to, so it no longer made much sense, it just seems like a random musing. Even worse I have often seen code deleted, and the comment remain. That is why comments that say what the code do are considered bad, they are pretty much redundant, as you can see what it is the code is doing, and then people change the code but not the comment and you end up with a comment saying “the below code does X” when in fact the below code is now doing Y.
I have seen, in assorted IT departments, then whenever they have an IT project they do a post mortem and come up with some “lessons” learned, which they then add to a big spreadsheet which nobody ever looks at again, and then the same mistakes are made the next time.
Taking my cynical hat off for a moment, maybe it would be an idea, that, whenever you find that something you have to do in regard to custom program maintenance could be placed in the “difficult to change” basket and so took a lot of work or whatever, try and ask yourself why it was difficult, and how you can redesign things so the next change is not so hard.
There are obvious ones like language – if you have hard coded EN or DE as the language and then have to make the program work for Poland or whoever, then you change the hard coding to SY-LANGU and then you don’t have to change it again for the next country.
The next most obvious is duplicate code – if you find you have to change six pieces of identical code in different places, then that is a signal to encapsulate the common code, so next time you only have to change it in one place.
I won’t go on with all the example, I am sure you get the idea, they range from obvious one like that and work up to complicated things like replacing conditional logic with subclasses or factories or something.
Again this is an antimatter idea – instead of the normal situation that every change makes the program more complicated so the next change is more difficult, every change makes the next one easier. Just like antimatter, when this sort of idea meets traditional ways of doing things, the entire universe explodes.
Let’s get back, back, back, back to the start
That was one hell of a diversion. You’ve probably already forgotten what caused me to go off topic – so I will summarise:-
· I have a burning desire to totally re-write old programs whenever I need to change small bits of them, what the OO gurus call “merciless refactoring”
· In the normal run of things this is suicidally dangerous, you are bound to break something and find yourself on the dole queue
· However, with sufficient unit tests in place you can make changes with impunity, and be sure you haven’t broken anything.
· Writing those unit tests is one hell of an effort. I personally think it’s worth it.
Now, let us return to the topic of state machines. I had an existing purportedly re-usable state machine template, I am going to extract the re-usable bits to Z classes, change them, and then see if the original program still works. I will be able to tell if the original program still works because it is 100% covered by unit tests (that’s not as impressive as it sounds, as it only does one thing, so only has one test).
Please don’t bother trying to find it – it’s not there!
So, what’s missing from my “gothic” ABAP program? This fine day I was on the train reading about state machines in the appendix to “Head First Object Orientated Analysis and Design”. The irony is I am the only one on the train reading a book made of paper, every single other person is playing with their electronic device. There was a tramp on the train the other night, drinking straight from a bottle of spirits and arguing loudly with the voices in his head, and even HE had a tablet of some sort. The only reason I’ve got an IPAD is that I won one at an SAP conference. Maybe that is where the gentleman on the train got his from.
Anyway, in the “Head First” description of state diagrams they say that when you have an arrow pointing from one state to another, you can have a “guard expression” which means you can’t follow that arrow unless the statement is true. There are no such conditions in the “Gothic” program, but I need them in the “Gumball” program – for example you can’t dispense a gumball if the machine has run out of gumballs. This is going to be such a common requirement it needs to be catered for in my re-usable Z classes.
The other main difference is that in the “Gumball” example there are all sorts of error messages shown to the user (of the machine) when they do something wrong like try and pull the dispensing lever without having entered any money first. In the “Gothic” program there is no output to any user whatsoever. So that needs to be addressed as well.
Having unit tests also forces you to do things “properly”. For example, you can’t have any sort of output to the user during a unit test, not even a pop up information message – it makes the test fall over – so you have to isolate the user interface layer into its own class and have a fake in the unit test so no actual user interaction takes place. So when the user does something that is not allowed I will have to throw an exception, and in the real code handle that by popping up an error message, and in the unit test just catch the exception and call an appropriate ASSERT method.
Class of the Mohicans
To start off with I am going to re-create the local classes in the “Gothic” program as Z classes and try and restrain myself from changing much just yet.
The first class I will call ZCL_SM_ABSTRACT_EVENT on the grounds that Martin Fowler named it “abstract event” though it isn’t really an abstract class. This was how it looked when it was a local class:-
CLASS lcl_abstract_event DEFINITION.
METHODS: constructor IMPORTING id_name TYPE string
id_code TYPE char04,
get_name RETURNING value(rd_name) TYPE string,
get_code RETURNING value(rd_code) TYPE char04.
DATA: md_name TYPE string,
md_code TYPE char04.
ENDCLASS.“Abstract Event Definition
As we are no longer in Java, we don’t really need the GET methods,we can make the attributes public and read-only, and set them in the constructor as per normal. I’ll make both attributes strings. The name is for humans to look at, the code is for an external system (i.e. a machine) to look at.
I then – in the Gothic program – replace the references to the local class with references to the Z class, and comment out the local class definition and implementation. I have to fluff around changing assorted data definitions from CHAR04 for a string but once I am done everything compiles. Next I take the Program -> Test -> Unit Test option to see if I have broken anything. I have not, so on I will go.
I then turn the two local classes for inbound events from external systems and outbound commands to external systems into ZCL_SM_EVENT_IN and ZCL_SM_COMMAND_OUT. These seems pointless at the moment, as all they do is inherit from the ZCL_SM_ABSTRACT_EVENT without changing anything, but that’s the way it was in the original program and I am not changing anything much yet.
Once again I comment out the definition of the local classes, do a find / replace exercise to bring in the Z classes, and once there are no syntax errors do the unit test thing again. I am going to take this as read from now on – I will be doing a unit test check after every change from now on, but won’t bore you by constantly saying so.
METHODS: poll_for_event RETURNING value(rd_event) TYPE string,
send_command IMPORTING id_command TYPE string.
This feels better, all the literature tells me interfaces are good, subclasses are bad. I turn this into a Z interface and change the parameter names to have the word CODE at the end, to remind me I am talking to an external system i.e. a machine. The actual implementation will always be different per program, as the external system will always be different.
I move onto to abstracting the “state” object into a Z class. Since the idea is to make the code read as much like English as possible I rename most of the methods. Here is the local class before I turn it into a Z class. I will be attaching a SAPLINK nugget at the end of this blog with all the code anyway.
CLASS lcl_state DEFINITION.
METHODS: constructor IMPORTING id_name TYPE string,
* Define Behaviour
state_reached_sends_command IMPORTING io_command TYPE REF TO zcl_sm_command_out,
state_changes_after_event IMPORTING io_event TYPE REF TO zcl_sm_event_in
io_target_state TYPE REF TO lcl_state,
* Execute Behaviour
changes_state_after_event IMPORTING id_event_code TYPE string
RETURNING value(rf_bool) TYPE abap_bool,
target_state_after_event IMPORTING id_event_code TYPE string
RETURNING value(rf_target_state) TYPE REF TO lcl_state,
send_outbound_commands IMPORTING io_external_system TYPE REF TO zif_sm_external_system.
DATA: md_name TYPE string,
mt_outbound_commands TYPE STANDARD TABLE OF REF TO zcl_sm_command_out,
mt_transitions TYPE g_tt_transitions.
I had to also extract the “transition” class at the same time, as the two classes depend on each other so much – a potential sign of bad design so I have been told.
CLASS lcl_transition DEFINITION.
METHODS: constructor IMPORTING io_source_state TYPE REF TO lcl_state
io_trigger_event TYPE REF TO lcl_event
io_target_state TYPE REF TO lcl_state,
get_source RETURNING value(ro_source) TYPE REF TO lcl_state,
get_target RETURNING value(ro_target) TYPE REF TO lcl_state,
get_trigger RETURNING value(ro_trigger) TYPE REF TO lcl_event,
get_event_code RETURNING value(ro_code) TYPE char04.
DATA: mo_source_state TYPE REF TO lcl_state,
mo_target_state TYPE REF TO lcl_state,
mo_trigger_event TYPE REF TO lcl_event.
I get rid of all the “get” methods, as in ABAP the same can be achieved with a public read-only attribute, I don’t need the GET_EVENT_CODE method at all.
With those two local classes extracted the “Gothic” program is looking a lot shorter, and there is still more we can take out. Remember we want to extract everything re-usable and only leave the specifics of this program.
The original program had a separate “state machine” class, which dealt with one thing only, which is handling reset events which bring the system back to its starting point. I can see that is a useful thing but I think I’ll rename it to better indicate what it’s purpose in life is.
That only leaves the “controller” class to be turned into a Z class. I checked all the method implementation and there are no commands that are specific to the application at hand, everything is nicely generic.
CLASS lcl_controller DEFINITION.
METHODS: constructor IMPORTING io_external_system TYPE REF TO zif_sm_external_system
io_system_resetter TYPE REF TO zcl_sm_system_resetter,
handle_command IMPORTING io_event_code TYPE string,
get_current_state RETURNING value(ro_state) TYPE REF TO zcl_sm_state.
DATA: mo_external_system TYPE REF TO zif_sm_external_system,
mo_system_resetter TYPE REF TO zcl_sm_system_resetter,
mo_current_state TYPE REF TO zcl_sm_state.
METHODS: transition_to IMPORTING io_target_state TYPE REF TO zcl_sm_state.
I will turn the “getter” method into a read only public attribute. I need to change the name of “handle command” to make it clear this is responding to an event coming into our system from an external system.
I am now down to only three classes – the external system, which naturally will be different every time, the class under test itself, in this case the gothic security system and the unit testing class. I like to think I have achieved one of the aims of OO – namely to separate the things that change from the things that stay the same. The low level “boiler plate” code has been broken down into classes so small they are completely generic and hidden in Z classes. The bulk of what remains in the application code itself is concerned with shouting to the rooftops what this program is supposed to achieve (the test class), and how it behaves to achieve that goal (the main configuration method).
Mind your Domain Specific Language
The next step is to ensure that what remains looks as much like plan English as possible, so our deadly enemies the business experts can read it. I used tobe a business expert myself, so I am my own worst enemy. This next level of separation could be described as “separating what can be read by a human from what can be read by an ABAPer”.
The first bit is knocking out the code which contains phrases like INHERITING FROM and INTERFACES and TYPE REF TO and hiding it from those who would not know what it means. They know not what it means, only that when they gaze upon the evil that is OO code, they know that it is A Bad Thing and must be purged from the land. We can isolate that in INCLUDES.
The next stage is making what remains read more like English. For example, as things stand at the moment we have:-
unlocked_panel_state->state_changes_after_event( io_event = panel_was_closed
io_target_state = idle ).
In the above “sentence” we have the words “state” and “event” twice in a row, prompting the comment “I heard you the first time” and the habit I have got into of prefixing input parameters for object instances with IO_ has to go in the dustbin. I still say there are times when you need that sort of thing, but not here, it just confuses the non-ABAP reader. The only business experts who I know of who like that naming convention are the Seven Dwarves. They wanted me to write the parameter as follows:-
IO_IO = ITS_OFF_TO_WORK_WE_GO.
After renaming a few object definitions (of states) and the name and parameters of a method in the ZCL_SM_STATE class the code reads a lot more like an English sentence:-
panel_is_unlocked->state_reached_sends_command( unlock_the_panel ).
panel_is_unlocked->state_reached_sends_command( lock_the_door ).
panel_is_unlocked->state_changes_after( event = panel_was_closed
to_target_state = idle ).
This constant meddling with things is a bit like the Japanese “Kanban” exercise, which, if I’ve got it right, means making a non-stop stream of really minor improvements to something forever and never being satisfied with the end result.
So I’m happy enough now, the next part is the vitally exciting part of the exercise, seeing how easy it is to assemble a “Gumball” program out of this framework with hopefully minimal effort. The first thing I’ll do is – hang on, what’s that coming over the hill? Is it a monster? Is it a monster?
Will the gumball machine program ever get written?
Will the monster destroy everything?
Will it’s “always done it this way” programming style and big teeth quash the OO revolution?
To find out, tune in to the next exciting episode of “Enemy of the State Machine” coming to an SCN near you in a month or so…
P.S. I tried to attach a SAPLINK nugget with the example “Gothic” program and the tiny Z classes needed to make it run, but it looks like SCN does allow the uploading of files of type .NUGG. Why would it? It’s not as if this is an SAP centric website or anything. I’ll attach the nugget file as a text file and see if anyone can work with that.