Best practices for objects creation with cascades of inherited creation procedures

33 views
Skip to first unread message

Gachoud Philippe

unread,
Jan 26, 2020, 4:46:12 PM1/26/20
to Eiffel Users
Hi Folks,

To begin, I don't know how to formulate my question...

The starting point
In my context, I'm messing with "Variable not properly set" which annoys me, but a great thk to the compiler which is doing the messy job of Void safety without him and the great constructors of this machinery I'd spend years finding in the execution stack where my errors are...

The point
is there a methodology to "correctly" call Precursors to avoid having to remember which attribute to set from ancestors in the correct order without forgetting any!

My strategy till now
make, make_from, ... => calling default_create into most of my creation procedures and then call setters like make_from_db_service is do default_create; set_from_db_service end

My interrogations

1. First one name it repeatedly called inherited feature
Trying to find why some attribute was not created, I wrote (copy pasted in fact) the default_create stack of a concrete entity of my application and discovered that there are many things which are done many times following the above strategy. 

      Illustration of that
    class A

        default_create
            do
                Precursor -- ANY
                create turtle
            end

    end -- class A

    class B

    inherit
        A

        default_create
            do
                Precursor {A}
                create rabbit
            end


    end -- class B

    class C

    inherit
        A
        B

        default_create
            do
                Precursor {A}
                Precursor {B}
                create cat
            end


    end -- class B

In the above case, the turtle from class A will be created 2 times! isn't it?

So on that case, it's pretty simple, you could do on C
    class C

    inherit
        A
        B

        default_create
            do
                create turtle
                create rabbit
                create cat
            end


    end -- class B

but on more complex cases, I don't want (and don't tell me I have to please...) each time I have 1 common ancestor go to see what is common on the creation procedure and then do it only once....


2. The second one
will follow because I still cannot identify it really well but the way to patch it (for the compiler who does understand better what's happening on the creation order than me) is creating attributes on beginning of the creation procedure when the compiler complains :-(((

Thx for your feed-back, I unfortunately did never have a senior Eiffel or good OO developer that could teach me the complexity of these things and discovering them myself....





Finnian Reilly

unread,
Jan 27, 2020, 12:53:42 PM1/27/20
to eiffel...@googlegroups.com
On 26/01/2020 21:46, Gachoud Philippe wrote:
but on more complex cases, I don't want (and don't tell me I have to please...) each time I have 1 common ancestor go to see what is common on the creation procedure and then do it only once....

One strategy is to use one of the reference attributes as a test for initialization. As for example

class A
      default_create
         do
            if not attached turtle then
               Precursor -- ANY
               create turtle
            end
         end

   end

But this will only work if you are using the non-void safe compiler and you know the attribute is always supposed to be attached (after initialization)

Eiffel-Loop has a general solution to the problem which works if you just want to assign some default values. It uses the class EL_REFLECTIVELY_SETTABLE to reflectively set each attribute. `make_default' sets default values for many common types as for example: STRING, STRING_32, ZSTRING, EL_FILE_PATH, DATE, TUPLE [X, Y, Z..] (where X, Y, Z are basic or common types) or objects conforming to EL_MAKEABLE. Using your example with EL_REFLECTIVELY_SETTABLE would look like the following. (Note that `new_instance_functions' is only redefined for attributes that are initialize-able using default methods)

class A

inherit
   EL_REFLECTIVELY_SETTABLE
      rename
         make_default as make
      redefine
         new_instance_functions
      end

create
   make

feature {NONE} -- Implementation

   new_instance_functions: like Default_initial_values
      do
         create Result.make_from_array (<<
            agent: like turtle do create Result end
         >>)
      end
end

class B

inherit
   A EL_REFLECTIVELY_SETTABLE redefine new_instance_functions end

create
   make

feature {NONE} -- Implementation

   new_instance_functions: like Default_initial_values
      do
         Precursor + agent: like rabbit do create Result end
      end

end

class C

inherit
   A B EL_REFLECTIVELY_SETTABLE redefine new_instance_functions end

create
   make

feature {NONE} -- Implementation

   new_instance_functions: like Default_initial_values
      do
         Precursor + agent: like cat do create Result end
      end

end
-- 
SmartDevelopersUseUnderScoresInTheirIdentifiersBecause_it_is_much_easier_to_read

Gachoud Philippe

unread,
Jan 27, 2020, 1:33:45 PM1/27/20
to Eiffel Users
Thx a lot Finnan, so it seems that you also had the same problems with that so much that you developed a library for managing that. I'll take a look deeper on that.

I was thinking there was some other solutions to that problem and of course taking void_safety in consideration...

Anybody else?

Gachoud Philippe

unread,
Jan 27, 2020, 1:42:57 PM1/27/20
to Eiffel Users
Am I the only one encountering this problem? has anything been thought in terms of language or compiling structure on this topic? where ca I find some more documentation on that topic?

thx in advance

Finnian Reilly

unread,
Jan 27, 2020, 2:25:05 PM1/27/20
to eiffel...@googlegroups.com
On 27/01/2020 18:33, Gachoud Philippe wrote:
Thx a lot Finnan, so it seems that you also had the same problems with that so much that you developed a library for managing that. I'll take a look deeper on that.

I had one further idea today that I quickly implemented without testing but, it looks like it might work. The class EL_INITIALIZEABLE has no dependencies except ISE base so you are welcome to try it. Here is how it is intended to be be applied. It is easy to imagine how this might be built into the compiler. The `Initialization_mask_table' must be implemented as a once function for each class heirarchy.

class A

inherit
   EL_INITIALIZEABLE

create
   make

feature {NONE} -- Initialization

   make
     do
         if not_initialized ({A}) then
            create turtle
            set_initialized
         end
     end

feature {NONE} -- Constants

   Initialization_mask_table: HASH_TABLE [NATURAL, INTEGER]
      once
         create Result.make (7)
      end

end

class B

inherit
   A redefine make end

create
   make

feature {NONE} -- Initialization

   make
     do
         if not_initialized ({B}) then
            Precursor {A}
            create rabbit
            set_initialized
         end
     end

end

class C

inherit
   A redefine make end
   B redefine make end

create
   make

feature {NONE} -- Initialization

   make
     do
         if not_initialized ({C}) then
            Precursor {A}
            Precursor {B}
            create cat
            set_initialized
         end
     end

end


-- 
SmartDevelopersUseUnderScoresInTheirIdentifiersBecause_it_is_much_easier_to_read

Finnian Reilly

unread,
Jan 27, 2020, 2:27:26 PM1/27/20
to eiffel...@googlegroups.com

Thx a lot Finnan, so it seems that you also had the same problems with that so much that you developed a library for managing that. I'll take a look deeper on that.

That reflection library has a lot more benefits besides. The initialization aspect is only the tip of the iceberg.

-- 
SmartDevelopersUseUnderScoresInTheirIdentifiersBecause_it_is_much_easier_to_read

Finnian Reilly

unread,
Jan 29, 2020, 5:09:02 AM1/29/20
to eiffel...@googlegroups.com

Class EL_PRECURSOR_MAP

The class EL_INITIALIZEABLE has some performance issues as you need to create a new TYPE object (requiring additional GC) unless you want to go to the trouble of caching the {TYPE}.type_id as a once function.

But I have come up with a better solution that is much more efficient. See class EL_PRECURSOR_MAP (source on github) Basically it uses the address of the make routine to look up a bitmap mask for a bitmap indicating whether the routine has been called already or not.

The class is used like this:

class A

inherit
   EL_PRECURSOR_MAP

create
   make

feature {NONE} -- Initialization

   make
     do
         create turtle
     end

feature {NONE} -- Constants

   Done_mask_table: HASH_TABLE [NATURAL, POINTER]
      once
         create Result.make (7)
      end

end

class B

inherit
   A redefine make end

create
   make

feature {NONE} -- Initialization

   make
     do
         if not done ($make) then
            Precursor {A}
            create rabbit
            set_done ($make)
         end
     end

end

class C

inherit
   A redefine make end
   B redefine make end

create
   make

feature {NONE} -- Initialization

   make
     do
         if not done ($make) then
            Precursor {A}
            Precursor {B}
            create cat
            set_done ($make)
         end
     end

end

Finnian Reilly

unread,
Jan 29, 2020, 5:46:28 AM1/29/20
to eiffel...@googlegroups.com

Class EL_PRECURSOR_MAP

I have introduced 2 variations of this class

  1. (EL_PRECURSOR_MAP_16) minimizes amount of memory for `done_bitmap' for small hierarchies of <= 16 members
  2. (EL_PRECURSOR_MAP_64) allows hierarchies of upto 64 members.
Reply all
Reply to author
Forward
0 new messages