Perl-Sensitive Sunglasses | |
PerlMonks |
Modules design pattern: abstraction vs incarnation (providing not so static data)by Discipulus (Canon) |
on Nov 20, 2020 at 12:23 UTC ( [id://11123892]=perlmeditation: print w/replies, xml ) | Need Help?? |
Hello monks, this post is a follow up of Module design for loadable external modules containing data but, after I squeezed my mind to spot the core of the problem and after finding some valid approach, I think it is worth a new Meditation.
A dark corner where the problem raiseIn the past when I found myself, facing an error or an unexpected behaviour, thinking: "it is perl or it is me?" it was always my fault. This time maybe not. Or maybe this is a problem in the way I imagine new things and perl does not make it easy as usual. What I mean is: modules are intended as an abstraction of behaviours and scripts are generally the incarnation of these behaviours in the real life. A module can be loaded and can export subs or methods. A module can be tested because generally it does nothing but exporting abstract behaviours. Only in few rare cases a module exposes data and if so it is just some bare package variable intended to modify its internal behaviour like $Data::Dumper::Indent and even this simple use can be accomplished in other, nowadays preferred, ways like providing parameters to the constructor and providing accessors for these kind of things. What happens if I need a new design pattern? I still need an abstraction, obviously, but I also need a serie of incarnations to be loaded indipendently upon request. These are not plugins that extend the main module with new functionalities: they are different incarnations of a mother abstraction. In the Perl::Teacher example I need the abstraction of Perl::Teacher::Lesson but then I need the incarnation of Perl::Teacher::Lesson::first_lesson and Perl::Teacher::Lesson::second_lesson and so on. Another project of mine ( my mad and fun Game::Term :) is stucked exactly for the very same reason. Infact I started to code ignoring the above problem and I have designed it to have incarnations (game scenarios in this case) as standalone scripts and this approach lead me to shell out when changing the current scenario, messing the whole thing (shelling out it is always a bad thing and behaves very differently on different platforms like linux and windows). A note about data: in the current post and in the previous one with data I intend not static data but a possible longish serie of perl data as others objects, anonymous subroutines, mixed with some (few) more static fragments as texts and questions.
Abstraction/Incarnation and OO roles
I was suggested to use a role for this. Honestly I'm not a big fan of OO perl frameworks, or better saying it, I will be a big fan when I will have the need to use all the feautures they provide.
A new design pattern?What I imagined is an orchestrator, a super-object (in this case of the Perl::Teacher class, but the same is valid for the Game::Term project) instantiated inside a perlteacher.pl program shipped within the main distribution. This super-object will be able to do many things related to all teaching activities (reading configuration file, interact with the user..) and its Perl::Teacher distribution will include the abstraction of what a course, a lesson, a talk, a question and a test are. But the main activity will be to load a course and its lessons in sequence. Real courses are just containers of real lessons and are shipped separetly from the main distribution. Here is the new design pattern I see. If you look at solution I provide below, you will see I prefere to use the constructor of the incarnation module to ship the meat to the super object. This pattern is vaguely similar to routes in modern web frameworks: fatty subroutines of behaviours and data. Well... in web programming we were told to separate the logic from the presentation and this sounds sane. I think my case is a bit different because I have mainly perl data (objects of other classes like Perl::Teacher::Test or Perl::Teacher::Question filling a lesson) in my incarnations. There is not static data to serve (as templates of html in the case of web programming) and for this reason I dislike the idea to have external, yaml or json, files containing the data to be served. Infact to use static external files I have to strictly describe their format and I loose the flexibility of a perl module (for example a lesson can provide a special kind of test defining a custom sub or loading another module). Here I present some sketch of my approaches, for sake of semplicity not in seprated .pm files but I think you will get an idea.
Possible approaches
First option: use the constructor of the incarnation module
This is my preferred one.
Second option: using a bare EXPORT in the incarnation module to provide dataThe abstraction module does not need to provide methods to its children. The abstraction module becomes an incarnation loading a data structure from My::Lesson::Example
Note: first and second approach can be mixed letting the incarnation module authors to use the interface they prefere. Something like this should be enough:
third option: static dataFile::ShareDir::Install allows you to install read-only data files from a distribution and File::ShareDir will be able to retrieve them. For reasons explained above I dont intend to use this solution for the Perl::Teacher project, but can be a viable solution, for example, to provide static maps to my scenarios in the Game-Term project. Thanks to kcott to show these modules. Anyway I dont see any advice against using __DATA__ in a module (if it is not a enormous amount of data): it will be accessible from within the module itself or from outside specifying the package name as described in Special Literals (taking care to close the filehandle when finished).
Conclusions
It seems very weird to me that this design problem does not occured before. As always there is the possibility that it is me :) The ability to switch from an incarnation to another, at runtime, in Perl is only available through modules. So I need an easy way to provide this mechanism and a clear interface to propose to incarnation modules authors, if any. I think my preferred solution, providing the meat in the incarnation module constructor, it is not so nice as interface for eventual authors but is the more perlish one I found at the moment. Any comment, suggestion, inspiration will be welcome. Thanks for reading. L*
There are no rules, there are no thumbs.. Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Back to
Meditations
|
|