From: Robert Klemme Date: 2007-01-10T06:20:11+09:00 Subject: Re: code organisation help request --------------090305010505030200050603 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit On 08.01.2007 20:28, Paul van Delst wrote: > Robert Klemme wrote: >> On 08.01.2007 16:50, Paul van Delst wrote: >> I would keep FDefMod as driver (i.e. the user instantiates it and >> invokes a method that does *all* the work. I'd then go on to create a >> class containing configuration information only (this is filled from >> the file; an instance of OpenStruct or Hash might be sufficient >> depending on your config info). Then I'd go on creating three classes >> for the three output types if there is enough different code to >> justify this. You could then connect those classes in any way that >> seems reasonable (i.e. have a common base class that contains shared >> code and state, create a module and include it in all three or other). > > O.k., I think I grok your meaning, but some implementation details still > escape me (I'm exposing my ignorance of both OO and ruby below, so > please bear with me.... and no laughing. Groans of disbelief, however, > are acceptable :o). This is what I came up with: > > In file basemodule.rb: > > module BaseModule > class BaseClass > attr_accessor :name > def initialize > @name="" > end > def self.basem > puts("This is class method basem") > end > end > end Why do you have a module in this file and not in the other files? > In file derivedclass.rb > > require 'basemodule' > class DerivedClass > include BaseModule > def self.output(obj) > puts("The obj class : #{obj.class}") > puts("The config info is: #{obj.config.inspect}") > end > end > > > and in mainclass.rb > > require 'basemodule' > require 'derivedclass' > class MainClass > include BaseModule > attr_accessor :config > def initialize > @config=BaseClass.new > end > def output > DerivedClass.output(self) > end > end > > ?? > > Now the above is very messy, so I obviously still need some guidance. Yes, you should start with proper class names. That makes things much easier. > One thing I find particularly odious is that to get the above to work, I > needed to make the DerivedClass methods class methods rather than > instance methods. You probably got inheritance and module inclusion mixed up. > Further enlightenment would be appreciated. class Base def foo() end end class Derived < Base # works: def bar() foo() end end module Foo def forx() end end class Includer include Foo # works: def barx() forx() end end >> Your FDefMod.create_struct(file) will then instantiate those three >> classes while it goes along and those instances of your output >> generation classes will then create output files. >> >> If you would want to classify this with a pattern name it's a bit like >> nested command object pattern, i.e. you have a major command and sub >> commands (i.e. one per output type). > > That is what I was thinking to do. Sometimes, I just need the definition > module, but not the I/O ones. I've attached a file to describe what I mean. You can distribute that code to multiple files (you'll have to open and close the module once per file). If you need to do some preprocessing common to all outputs, you might want to add a PreProcessor class that converts a Config into something else that stores the preprocessed state (and maybe also references the original config). You can then pass that on to individual generators. >> Of course, this is said with the little we know about your code. >> There might be potential for further refactorings in the code you have >> (i.e. split up methods into several methods, identify common code / >> patterns and extract them into additional methods that are invoked in >> multiple places etc.). > > That is exactly my plan. For each of the so-called "derived classes", > the same procedure can be applied again. For example, if my f95 > structure is named "MyStruct", then the f95 definition module that is > created, MyStruct_Define.f90, would contain the public procedures, > Associated_MyStruct: Check if all the pointer components are associated > Create_MyStruct : Allocate the pointer components of the structure > Destroy_MyStruct : Deallocate the pointer components > Assign_MyStruct : Deep copy the structure (a simple assignment just > copies > the pointer references, not the actual data) > Equal_MyStruct : Determine if two structures are equal > Info_MyStruct : Print out info on the structure dimensions Are you talking about Fortran or Ruby code here ^^^^? I was talking about Ruby code *only*. > The ruby methods to create each individual f95 procedure could be put > into its own class with one public instance method and a bunch of > private methods (for formatting particular things in the output > procedure.) I could have a separate file (class?) that handles the > creation of each of the above procedures in the f95 module. Same for the > i/o stuff. It might make unit testing each part easier too. > > There are some methods that are used in several places to create parts > of just the f95 definition module, just as there are common methods used > to create parts of just the f95 I/O module; and then there are common > methods used in formatting output for all three f95 modules. Are you doing this in two steps, i.e. create Fortran modules and then format and output them? Assuming yes, this does not seem to make much sense to me - you can create the Fortran code formatted right from the start, can't you? > As I added functionality, I've been refactoring a lot - that's another > reason I want to split the code into smaller bits: it should allow for > much easier identification of the common parts (at least, I think so.) Yep. >> Btw, how many LOC are we talking about? > > Hardly any. About 3K loc. In the future I think that can be reduced > quite a bit since there is still a fair amount of boilerplate Fortran95 > code in there that is just getting dumped via herefiles. Sounds good, i.e. still early enough that it's feasible and you can try out variants. Kind regards robert --------------090305010505030200050603 Content-Type: text/plain; name="sample.rb" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="sample.rb" # module for namespace purposes only module FortranOutput class Config attr_accessor :name, :foo def self.load(file_name) config = new File.foreach(file_name) do |line| # parse content line by line end config end end # base class with all shared bits of code class OutputBase attr_accessor :config end # output for Struct (whatever that means) class StructOutput < OutputBase def generate puts self.class end end # output for algorithms (whatever that means) class AlgorithmOutput < OutputBase def generate puts self.class end end # main driver of the generation class Driver # generators for individual parts GENERATORS = [ StructOutput, AlgorithmOutput, ] def generate(config_file_name) config = Config.load(config_file_name) GENERATORS.each do |gen_class| gen = gen_class.new gen.config = config gen.generate end end end end # silly main FortranOutput::Driver.new.generate __FILE__ --------------090305010505030200050603--