Difference between revisions of "Enums in Eiffel"
m (→Enums for Brevity) |
m (→Enums for Brevity) |
||
Line 133: | Line 133: | ||
end | end | ||
+ | </code> | ||
At first look out of seven features it has been reduced to only two. That alone is a major change in the amount of code that has to be written for a library author. The <code>[eiffel]EV_TEXT_ALIGNABLE</code> is actually deferred so that is also a lot less code an implementor has to write. It does not stop there, with the type safety introduced, by changing <code>[eiffel]text_alignment</code> to use a Enum type instead of an integer, the class invariant has been remove as have the reference comments needed in <code>[eiffel]text_alignment</code> to explain exactly which class needs to be used, containing the constants, to use <code>[eiffel]text_alignment</code> correctly. | At first look out of seven features it has been reduced to only two. That alone is a major change in the amount of code that has to be written for a library author. The <code>[eiffel]EV_TEXT_ALIGNABLE</code> is actually deferred so that is also a lot less code an implementor has to write. It does not stop there, with the type safety introduced, by changing <code>[eiffel]text_alignment</code> to use a Enum type instead of an integer, the class invariant has been remove as have the reference comments needed in <code>[eiffel]text_alignment</code> to explain exactly which class needs to be used, containing the constants, to use <code>[eiffel]text_alignment</code> correctly. | ||
Line 140: | Line 141: | ||
on_text_selected | on_text_selected | ||
+ | == An Example == | ||
+ | |||
+ | <code>[eiffel] | ||
+ | feature {NONE} -- Initialization | ||
+ | |||
+ | create_alignment_buttons | ||
+ | -- Create alignment group tool bar buttons | ||
+ | do | ||
+ | add_alignment_button (once "left", {EV_TEXT_ALIGNMENT_CONSTANTS}.left) | ||
+ | add_alignment_button (once "center", {EV_TEXT_ALIGNMENT_CONSTANTS}.center) | ||
+ | add_alignment_button (once "right", {EV_TEXT_ALIGNMENT_CONSTANTS}.right) | ||
+ | end | ||
+ | |||
+ | add_alignment_button (a_name: STRING; a_alignment: INTEGER) is | ||
+ | -- Creates an adds an alignment tool bar button | ||
+ | require | ||
+ | a_name_attached: a_name /= Void | ||
+ | not_a_name_is_empty: not a_name.is_empty | ||
+ | valid_alignment: (create {EV_TEXT_ALIGNMENT_CONSTANTS}).valid_alignment (a_alignment) | ||
+ | local | ||
+ | button: EV_BUTTON | ||
+ | do | ||
+ | create button.make | ||
+ | button.set_pixmap (pixmap_loader (once "button_" + a_name)) | ||
+ | button.click_action.extend (agent on_alignment_button_clicked (a_alignment)) | ||
+ | alignment_group.extend (button) | ||
+ | end | ||
+ | |||
+ | feature {NONE} -- Event handlers | ||
+ | |||
+ | on_alignment_button_clicked (a_alignment: INTEGER) is | ||
+ | -- Called when an alignment tool bar button is selected | ||
+ | require | ||
+ | valid_alignment: (create {EV_TEXT_ALIGNMENT_CONSTANTS}).valid_alignment (a_alignment) | ||
+ | local | ||
+ | l_alignable: EV_TEXT_ALIGNABLE | ||
+ | do | ||
+ | l_alignable ?= selected_widget | ||
+ | if l_alignable /= Void then | ||
+ | inspect a_alignment | ||
+ | when {EV_TEXT_ALIGNMENT_CONSTANTS}.left then | ||
+ | l_alignable.align_text_left | ||
+ | when {EV_TEXT_ALIGNMENT_CONSTANTS}.center then | ||
+ | l_alignable.align_text_center | ||
+ | when {EV_TEXT_ALIGNMENT_CONSTANTS}.right then | ||
+ | l_alignable.align_text_right | ||
+ | |||
+ | -- No else because if a new alignment is | ||
+ | -- added we want an exception to be raised. | ||
+ | |||
+ | end | ||
+ | end | ||
+ | end | ||
</code> | </code> |
Revision as of 14:28, 3 May 2007
Preface
Something pondered for years was the question regarding why Eiffel has never embraced Enum types or a variant more in style with the Eiffel paradigm. There are a number of comments from numerous developers regarding why Enums are "Bad". Most languages exhibit bad ideals and some more than others. The congisen is that if you give a developer a tool to abuse it will be abused and sometimes by the seasoned developers. Generally, seasoned developers have a grasp of the dangers of abusing aspects of a language to gain performance or micro design. However lessons are learned from those who know better from those that know less. As such bad programming practices creep down the chain until it become a common convention.
In this document I'll outline the pontetial dangers and the oddities found commonly with the "Enum" type and attempt to dispell them with a solution to implementing Enums in Eiffel. First and foremost, Why does Eiffel need an Enum type...
Why Does Eiffel Need an Enum Type
The reasons are numerous and leaves us asking why they were not approached in the ECMA specification.
As Eiffel evolves and rears its head into the mainstream, its application domain expands. Users, libraries and complexity all grows as a languages does. It has been long said that Eiffel is almost unique in its ability to self-document classes and routines through terse comments and contracts. However, Eiffel for being so terse with commenting, is extremely verbose with class interfaces which can be trying at times.
Enums for Brevity
To demonstrate a point of Eiffel's verboseness, due to the lack of an Enum type specification, turn you eyes to EV_TEXT_ALIGNABLE
:
deferred class EV_TEXT_ALIGNABLE feature -- Access text_alignment: INTEGER is -- Current alignment. -- See class EV_TEXT_ALIGNABLE_CONSTANTS for -- possible values. require not_destroyed: not is_destroyed ensure bridge_ok: Result = implementation.text_alignment feature -- Status report is_left_aligned: BOOLEAN is -- Is `Current' left aligned? require not_destroyed: not is_destroyed is_center_aligned: BOOLEAN is -- Is `Current' center aligned? require not_destroyed: not is_destroyed is_right_aligned: BOOLEAN is -- Is `Current' right aligned? require not_destroyed: not is_destroyed feature -- Status setting align_text_center is -- Display `text' centered. require not_destroyed: not is_destroyed ensure alignment_set: is_center_aligned align_text_right is -- Display `text' right aligned. require not_destroyed: not is_destroyed ensure alignment_set: is_right_aligned align_text_left is -- Display `text' left aligned. require not_destroyed: not is_destroyed ensure alignment_set: is_left_aligned invariant valid_alignment: (create {EV_TEXT_ALIGNMENT_CONSTANTS}).valid_alignment (text_alignment) end
In EV_TEXT_ALIGNMENT
there is already is text_alignment
, which exhibits the inherent problem with type saftey through the lack of an Enum type. No only is a flag attribute present but there are the status setting routines align_text_left
, align_text_right
and align_text_center
. On top of that, for the sake of code clarity for clients there are the status queries is_left_aligned
, is_right_aligned
and is_center_aligned
. The status setting and query routines hide the implementation details of having to know and use EV_TEXT_ALIGNMENT_CONSTANTS
, which is a good thing but can also be very frustrating when writing effective code using this interface.
To demonstrate, assume a graphical editor has been developed using EiffelVision2. In the editor the user selects a region of text which should enabled tool bar button used to manipulate the alignment of the selected region to text.
on_text_selected require has_selection: has_selection local l_alignable: EV_TEXT_ALIGNABLE do l_alignable ?= selected_entity if l_alignable /= Void then if l_alignable.is_left_aligned then active_button := left_aligned_button elseif l_alignable.is_center_aligned then active_button := center_aligned_button elseif l_alignable.is_right_aligned then active_button := right_aligned_button else -- New alignment not respect! check False end end end if active_button /= Void alignment_button_group.set_active_button (active_button) alignment_button_group.enable_sensitive else alignment_button_group.disable_sensitive end end
Using an Enum type for EV_TEXT_ALIGNMENT
would change things dramatically. First off,
deferred class EV_TEXT_ALIGNABLE feature -- Access text_alignment: EV_TEXT_ALIGNMENT assign set_text_alignment -- Current alignment. require not_destroyed: not is_destroyed feature -- Status setting set_text_alignment (alignment: like text_alignment) -- Set `text_alignment' to `alignment' require not_destroyed: not is_destroyed ensure alignment_set: text_alignment = alignment end
At first look out of seven features it has been reduced to only two. That alone is a major change in the amount of code that has to be written for a library author. The EV_TEXT_ALIGNABLE
is actually deferred so that is also a lot less code an implementor has to write. It does not stop there, with the type safety introduced, by changing text_alignment
to use a Enum type instead of an integer, the class invariant has been remove as have the reference comments needed in text_alignment
to explain exactly which class needs to be used, containing the constants, to use text_alignment
correctly.
on_text_selected
An Example
feature {NONE} -- Initialization create_alignment_buttons -- Create alignment group tool bar buttons do add_alignment_button (once "left", {EV_TEXT_ALIGNMENT_CONSTANTS}.left) add_alignment_button (once "center", {EV_TEXT_ALIGNMENT_CONSTANTS}.center) add_alignment_button (once "right", {EV_TEXT_ALIGNMENT_CONSTANTS}.right) end add_alignment_button (a_name: STRING; a_alignment: INTEGER) is -- Creates an adds an alignment tool bar button require a_name_attached: a_name /= Void not_a_name_is_empty: not a_name.is_empty valid_alignment: (create {EV_TEXT_ALIGNMENT_CONSTANTS}).valid_alignment (a_alignment) local button: EV_BUTTON do create button.make button.set_pixmap (pixmap_loader (once "button_" + a_name)) button.click_action.extend (agent on_alignment_button_clicked (a_alignment)) alignment_group.extend (button) end feature {NONE} -- Event handlers on_alignment_button_clicked (a_alignment: INTEGER) is -- Called when an alignment tool bar button is selected require valid_alignment: (create {EV_TEXT_ALIGNMENT_CONSTANTS}).valid_alignment (a_alignment) local l_alignable: EV_TEXT_ALIGNABLE do l_alignable ?= selected_widget if l_alignable /= Void then inspect a_alignment when {EV_TEXT_ALIGNMENT_CONSTANTS}.left then l_alignable.align_text_left when {EV_TEXT_ALIGNMENT_CONSTANTS}.center then l_alignable.align_text_center when {EV_TEXT_ALIGNMENT_CONSTANTS}.right then l_alignable.align_text_right -- No else because if a new alignment is -- added we want an exception to be raised. end end end