Back to the Future – Part 03
Hopefully, this time the SCN site won’t post this blog before I’m finished, when I am mid way through typing a word. It happened the last two times though, so if you are reading this blog and it cuts off in mid-sentence and you think “that’s odd” then it is because the blog decided to publish itself when I pressed the bullet ICON or some such.
Anyway, in my first blog http://scn.sap.com/community/abap/blog/2012/05/01/back-to-the-future–part-01 I talked about what I am trying to achieve and the historical background which caused me to want to re-write an example application in a modern way.
In http://scn.sap.com/community/abap/blog/2012/05/09/back-to-the-future–part-02 I discussed steps I have already taken to try and make custom programs more “portable” between SAP systems.
In this blog I will start to discuss how I am going about building the new “modern” version of the example application. I am going to handle this in very bite sized chunks, going into detail at every stage.
Firstly, I will respond to feedback given from the last blog:-
Sets and the City
I would like to give thanks to the gentleman who pointed me in the direction of the transactions to create and maintain sets of values for use in ABAP programs i.e. GS01 to GS04. I was unaware of this functionality. Although I cannot see any immediate use at the moment – I don;t think it would add any benefit to my solution of global customising ranges I described earlier – every time I learn something new it is a good thing.
Likewise I was pointed to blogs about the enhancement framework, and after reading those the light bulb has finally gone on. It seems it is not a question of choosing between OO inheritance and the enhancement framework – in some ways they appear to be one and the same thing.
Anyone correct me if I have got the wrong end of the stick, but my understanding now is that an interface seems to be the SAP answer to multiple inheritance, and the various BADI implementations of that interface appear to fulfill the role of subclasses.
I was mucking about with dynamically building up the name of the subclass I wanted e.g. “ZCL_DELIVERY” + “AU” or something, but solutions based on developers getting the name of something right are always fraught with danger. Since the “filter” in the BADI implementation is standard SAP functionality that enables picking the correct subclass at runtime based on country or whatever, it would be crazy to re-invent the wheel.
As another example, I have a situation where I have a mapping class. 90% of the code is always the same, and there are a few extra twists needed fr some countries, and a few extra twists needed fr some devices that SAP communicates with. The question is, do I need two BADI implementations, one with a filter on country, one with a filter on device, or one implementation with two filters? I could have one situation with just Australian based extra logic, another situation with just Device A extra logic and the third situation which only occurs in Poland for Device C. Ideally you would want the minimum number of implementations.
Going back to my complicated test application, as I mentioned earlier, the chances are that a large number of developers will be working on parts of the application at the same time, so having separate repository objects – that is BADI implementations – with a stable interface should prevent them blocking each other. Mind you, Z subclasses would have the same advantage.
Seconds Out, Round Three
I am going to try and recreate my thought processes as I create the application step by step, to try and invite discussion at each stage. This will result in a sort of “stream of consciousness” such as James Joyce used in his book “Ulysses” in 1922.
OK, off we go – “Stately, plump Buck Mulligan came from the stairhead, bearing a bowl of lather on which a mirror and a razor lay crossed” – oh hang on, that was the actual first line of Ulysses. I should be talking more about ABAP.
Now I am back in Australia, I am like a bull in a china shop, trying to use all the new features in ECC 6.0 at once, regardless of whether the task at hand needs them or not. Hopefully after a while I will calm down.
It occurs to me that the very first thing to look at as I prepare to press the very first key within SE38 is the difference in the ABAP editor between 4.7 and ECC 6.0 – both used the new front end editor, but I was given to understand that code completion is a lot better now. Here is what I have discovered thus far.
- CTRL + SPACE = for classes this works like the “pattern” when creating function modules, for dictionary and – more importantly – self-defined structures local to the program, the code completion proposes a list of fields from which you can choose. You also get a big yellow box which seems to be for information only.
- CTRL + SHIFT + V = the editor remembers lots of items you have put on your clipboard in the recent past. SO I could copy three chunks of code from different places using CTRL C and then go to my target program. Pressing CTRL V on its’ own would just paste in the last entry, but pressing the SHIFT key in addition would give me a list of all the recent entries, and let me choose which one I want. I can’t remember how many times in the past I have tried to paste something in, only to realize that the contents f the clipboard are not what I expect.
- ALT + MOUSE = based on the equivalent function in Microsoft Word, this worked before, enabling you to copy a square or rectangle o code. I will use this when I want to copy a list of fields from a self-defined structure without the “TYPE vbak-vbeln” on the right hand side, so I can make sure I assign values to them all, for example.
I am sure there are tons more useful things you can do with the new code completion, if anyone can tell me any, or point me to useful (i.e. nt SAP) documentation, that would be lovely.
Less is More
When it comes to the content of the code I am going to write it turns out I need less of it. The next major change in ECC 6.0 is that you no longer
need anywhere near as many “helper” variables. It is now possible to say ( A + B ) > ( C + D ) which you could do in most languages 30 years ago, but not in ABAP up until now.
… becomes ….
Pragma, we’re all crazy now
The second code related change, especially for someone like me who keeps doing an extended program check every ten seconds, is how to suppress the assorted false errors raised by the SLIN exercise.
Every time I used to put in a “pseudo-comment” I wanted to write a comment near it in the code, saying why the “error” was not in fact an error, so I did this above the statement, or right at the end of a complicated set of selection criteria. This becomes a lot easier with pragmas. As I recall if a statement had two things wrong with it, you had to say #EC *, I think now you can have more than one pragma for the same statement, and I can comment each one.
OK, now lets’ write a program…
The first difference I encounter is that in OO world you don’t have one big monolithic program, such as I have been accustomed to writing. The whole structure has to be different. I hope SAP will not sue me for using the below diagram, it is many years old now and so most likely not a secret, so old in fact that it still mentions SOA which used to be in every SAP presentation, but never gets mentioned any more, it stopped right around the time they decided to synchronize the release cycles of the various systems in the business suite e.g. CRM / SRM etc…
As I understand it, the green acorns above relate to the “presentation layer” i.e. showing the data to the user. The yellow acorns are the application itself i.e. the “model”. The gray acorns are the “persistence layer” i.e. retrieving and storing data in the database. The pink acorns relate to interfaces with external systems. The idea is to be able to swap out any set of acorns and replace them with new ones that have the same “signature” and the other acorns don’t notice or care.
So, in the past the main ABAP program was the be-all and end-all. Now, it is going to be the “controller” and have far less coding in it than I am accustomed to. Its’ purpose now is just to show things to the user and pass information backwards and forwards from sort sort of UI technology (the view) to the model which is going to do the “hard yards” i.e. business logic and reading and updating the database.
This instantly raises a question from me. I would love to adopt the “Test Driven Development” methodology – i.e. you design the test before writing the program – but all the examples I have seen involve testing subroutines (methods) which do some sort of mathematical calculation. You simulate database reads or updates by “injection” of dummy classes, as you can’t unit test either of the latter two. In my experience, and it may just be my company, the average custom program is comprised as follows:-
Please correct me if I am wrong, but in the above example, if calculations are all you can unit test, it looks like the benefits of unit testing may be limited. Once again, please tell me if I am missing the obvious.
Anyway, I was bursting to try this, so I wanted to add a test class in my new SAP system on a BI extractor I had to write. That comprised of a database read, which is something you can’t unit test, exporting to an external system, which you also can’t unit test, and doing a bunch of data mapping, which you CAN unit test.
When the program is run for real we want to – obviously – actually read the database, and so when we create the object we do not pass in any import parameters.
When we run a unit test, we want to have dummy data, so we input an optional parameter LO_ACCESS_CLASS when creating the object, which will then cause the code to use a different class to get the data.
When you take the option Program > Test > Unit Test from SE38 all methods defined as FOR TESTING get runs, which is just one in my simple example.
So, the test class is calling the productive code for the data mapping, and dummy code for the database access. If any of the CL_AUNIT_ASSERT tests fail, you get a message after doing the “unit test” option. So, if you change the real code for data mapping, the test is automatically updated (as it uses the real code). Test code does not compile in production, so there is no extra overhead.
The idea is that after the extended program check I do the unit test option, just another quality check in the development process. In a complicated program I would have lots of test methods, adding more as the program gets more complicated. That way every change you make, you can do a regression test to some extent.
When I did this for real I was pleasantly surprised, as when I ran the unit test (before I even tried to read the database for real) assorted mistakes I had made were shoved in my face e.g. I had made the assumption that a notification number was ten digits long, like a sales order number. In real life it is 12 characters long. So, some logic I had written, extracting NUMBER+1(9) did not in fact return the last nine characters f the number, but nine numbers from the middle. There were a few others I won’t go into, but in essence this tool proved it’s worth instantly as far as I am concerned.
To re-iterate, this is a low level test for calculations and the like. Presumably one would use a tool like HPQC for the proper regression tests involving actually reading and writing from the database – I want to move to a situation where every time I make a change to a program I run an HPQC test – in DEV – when I have finished my changes and am thinking about telling our change control tool (we use RevTrac in Australia ) to say I have tested the ting. In fact it would be good if we had a programmatically enforced rule that a change could not go to production until an HPQC test had been run in the quality system, along with any relevant regression tests to do with the changed program.
I am a Model, You Know What I Mean
So, leaving aside unit tests, the first thing to do is to create the model. This will handle the business logic of the application, and will be used by my executable ABAP “controller” program which will display things in the SAP GUI. Then when I get my head around Web Dynpro I could create a controller for that which would use the same model. If I was being silly, which I always am, I could describe the SAP GUI as the fat controller, and the Web version as te thin controller.
In actual fact, and I am 100% sure I will be burned as a witch at the stake for saying this, the Web Dynpro screens I have seen demonstrated so far tend to look uglier than the ALV equivalent in the SAP GUI and run slower, but the first observation is a pure aesthetic judgement, and the second will be blamed on hardware or the network or some such. It is rather like when the “classic” ALV list which ran on the server was replaced with the ALV grid which ran on the client, and apart from lost more short dumps, the screen filled with white squares for a few seconds whenever you scrolled down. The ZX80 computer did something similar I recall. Anyway, that was a total digression.
One of the gentlemen commenting on my last blog stressed the importance of interfaces. I presume in this context, if you are going to live your life creating application after application, all of which have a certain basic subset of methods with the same interface (signature) e.g. returning an instance of the model, saying what toolbar commands are available, accepting user commands etc. then you would create an interface with the common methods, so you don’t have to keep coding the same thing again and again. Is that correct, or is that nonsense?
So, I will break off here, and play with creating the model and a controller that will make use of it, and then present my initial draft in my next blog. I will end with a few questions which I have been puzzling over.
Questions for the Outside World
- Once in the distant past I found an article describing the new ALV model – how everything was more unified now in one inheritance tree, and the best way to make use f this. I cannot find this article using the SCN search or via Google. Has anyone got any idea where I should look?
- I read in the Design Patterns book that when using the SAP GUI you always need to define a DYNPRO screen. is that still true with the new ALV object model? Say if I want to have two grids on the same screen?
- According to the MVC strategy the model should execute the business transaction, but the controller should output the result to the user as the model has no idea what user interface technology is being used e.g. as I understand it there is no MESSAGE statement in Web Dynpro. I was thinking of raising a class based exception when something goes wrong, so I can pass out the BAPIRET2 or BDCMSSCOL error table. However generally I output a “warm fuzzy feeling” message to the user to tel them what is going on, as the transaction takes a few seconds to execute, and one user command might fire a string of transactions. What do I do if the user command fires five transactions in a row and I want the user to see a message at the bottom of the screen before each of them, so they can track the progress of what is going on? Can the model raise some sort of event to tell the controller to output a progress message, and the continue merrily on it’s way?
That’s it for now, as always the more feedback the better, I would love to know which things I have got wrong, and if by some miracle I have understand any of the above correctly.
P.S. I just went through my blogs, correcting about one spelling mistake in each one, and ticked the “minor update, do not update stream” button, but they still went to the top of the “latest activity” list.
07/09/2013 – that aspect of SDN has still not been fixed…
Other OO Blogs:-
Interesting read. A comment, if you don't mind. While you refer to ABAP versions of 4.7 and ECC 6.0, the ABAP toolset and features are directly linked to the NetWeaver (and prior to that, WebAS) version, rather than to the R/3 back office or customer-facing products, like SEM, SCM, CRM, etc. The decoupling of ABAP from R/3 goes back to the introduction of WebAS 6.0 and R/3 v4.7. To my knowledge the last R/3 version with its own ABAP version was 4.6x (with Basis). So, think of technical tools, including ABAP, as NetWeaver versioned and largely independent of the R/3 (or more appropriately, ECC) and customer facing products installed.
I remember when we upgraded from 4.5 to 4.7 and SAP made much of the split between the "enterprise core" and the 620 system which replaced the BASIS layer. Hooray, we thought, now we can upgrade the ABAP stack independent of the enterprise core. No you couldn't. We enquired about going from extension set to 110 to 200, and were told that was an upgrade, as the 620 version was different. we enquired about just upgrading the 620 version and were told that was impossible also.
That seems tightly coupled to me.
So the question I have is - I am currently on ECC 6.0, EHP5. If I so desired could I just upgrade the Netweaver portion from 702 to 703 say, in the same way i would download the latest version of Java? If so, I will be happy as larry, that would mean that now as you say the ABAP version will be independent of the application level and all will be well in life.
I guess you have to create different filters for it. But what you can do is, you can implement an abstract class as an example class for the Badi, which implements the Badi-Interface. In that abstract class you can have the 90% of your code. Than you can inherit in your concrete implementation class from that abstract class and redifine only those methods which changes. This would minimize the implementation efforts.
Along the lines of "dynamically building up the name of the subclass...", what I did was on the parent class I created a static public method which returns a class instance. The return parameter is type ref to the parent class. A parent class can inherit the extra characteristics (i.e. methods) of a child class. The import parameter can be the country, for example. Logic goes something like this below. It does require some hardcoding. But you could also create a customizing table for the country mapping. Then whatever program needing to use this class would start by getting the instance. In the program, you only program for the instance returned. This eliminates IF/THEN statements within the programs themselves. The developer doesn't need to decide which class to use:
DATA i_AU TYPE REF TO zcl_main_AU.
DATA i_DE TYPE REF TO zcl_main_DE.
DATA i_UK TYPE REF TO zcl_main_UK.
CREATE OBJECT i_AU.
p_instance = i_AU.
CREATE OBJECT i_DE.
p_instance = i_DE.
CREATE OBJECT i_UK.
p_instance = i_UK.
CREATE OBJECT p_instance.
** Sample program code to get instance:
*--- Get instance according to country
p_instance = zcl_main=>create_instance( i_country = p_country).
I was doing something similar in Germany and I did indeed use a customising table to get the name of the class based on the country.
I am a bit obsessive compulsive with trying to get rid of hard coding!
I'm not too sure about that article you mention, but the SAP documentation on the new SALV model is excellent (Go to CL_SALV_TABLE ->Class Documentation)
Also the SALV*DEMO* example programs are there too.
About messages: the way to handle them is via events. You can create an interface with a method to handle that event, and then have a class implement that interface in various ways. If you use a static event, you don't need to pass the handler around everywhere either.
For instance, in online mode the event handler could directly display the messages, or collect them into a table or tree, but in background mode you could use a different class that writes the messages to a dataset somewhere. You can even have multiple handlers for the same event.
Either way, it keeps the layers seperated nicely, the application layer doesn't need to care what the presentation layer is, it just raises the event and continues on. You could theoretically use resumable exceptions, but this is not really the intended purpose.
Thanks for that, I used the new CL_SALV_TREE model the other day, and the example program was fine for demonstrating what needs to be done.
I suspected that events were the way forward in regard to the model telling the view what it is up to.
I wholeheartedley agree with not using exceptions for a dubious purpose, even if it would work!