Hello,
it seems to me there is no need to hide all data members. If no other data
values depend upon this one the declaration it as "protected" ot "private"
becomes "the strictness for the sake of strictness".
Thank you very much for 6.30.5 release.
The problem of writing and reading pointers to streamable objects under MSVS
2008 was fixed.
Igor
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
In the 6.31.04 development version the "public_data:" sections are changed to
be protected instead, unles building with OWL 5 compatibility.
Accessors are added and should be used instead of directly accessing the data
members.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi, I have a few reservations about this. There is a risk that we may reduce
the code quality, and actually introduce bugs, by providing accessors and
mutators indescriminately. Mutators in particular need special consideration.
I ran into a bug recently that was probably caused by the introduction of a
mutator; see "TPrinter::SetData leaks memory", bug #3128107. In this case the mutator failed to manage ownership
(memory management). In general, a properly implemented mutator should always
uphold the class invariant (valid state).
This is of course also a problem with public access to internal data. But it
becomes worse when the class provides a broken interface to internal details.
Most programmers are wary of manipulating internal members, even, or
especially, if the members are public. The latter is a strong signal that the
class interface is poorly designed. On the other hand, a programmer expects
that a mutator interface is safe and upholds the class invariant, e.g.
TPrinter::SetData above.
There is also the question of the signature of accessors and mutators. For
example, TPrinter::GetData and SetData returns and receives a pointer. This is
most likely because the data is held internally by pointer. But this is an
implementation detail, and it makes no sense for GetData to return a null-
pointer or SetData to receive a null-pointer. TPrinter will always maintain
ownership of a TData instance in its valid state; i.e. a valid data member is
part of the class invariant. So there is a strong argument for the accessor
and mutator to use references instead.
Some members are truly internal implementation details and should have no
interface at all. There are probably many programs out there (heart surgeants)
that relies on access to truly internal details of OWL classes. For these we
should continue to provide a build option that provide public access; with the
warning that the innards may change and become private in the future, and that
we, for that reason, deprecate the build option. It makes no sense to provide
an interface (GetHeart and SetHeart) to these internal details. Even if the
accessors and mutators are implemented correctly they still violate the
encapsulation principle in good design.
So I suggest that public data members are categorized:
part of the class interface; provide access via accessors and mutators; the latter only if strictly needed, and if so, ensure the implementation upholds the class invariant.
truly internal; hide and don't add an interface. The programmer should either use a compatibility build option, or rewrite the code to not rely on internal details.
Regards,
Vidar Hasfjord
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
So I suggest that public data members are categorized:
part of the class interface; provide access via accessors and mutators; the latter only if strictly needed, and if so, ensure the implementation upholds the class invariant.
truly internal; hide and don't add an interface. The programmer should either use a compatibility build option, or rewrite the code to not rely on internal details.
I agree. As a start, I created a list of the public_data members of most OWL
classes:
hPrevInstance - not used at all under 32-bit. There are Get and Set methods,
but the Set method makes no sense to be used.
nCmdShow - the value from WinMain() arguments. There are Get and Set methods,
but the Set method makes no sense to be used.
DocManager - There are Get and Set methods.
MainWindow - There are Get and Set methods. Cusrious thing is the the Set
method is protected.
LangId - There are Get and Set methods.
Tooltip - There are public Get and protected Set methods
TCheckBox
/// If the check box belongs to a group box (a TGroupBox object), Group points to
/// that object. If the check box is not part of a group, Group is zero.
TGroupBox* Group;
Has public Get and Set methods.
TComboBox
uint TextLimit; /// Maximum length of text in edit subcontrol
Has public Get and Set methods. The Set methos also sends CB_LIMITTEXT to the
window, so definitely it should be used
TDialog
TDialogAttr DialogAttr;
Has public Get and Set methods. Initialized in the constructors and used
during dialog creation - so maybe only the Get method is needed.
TDocument///Holdsapointertotheapplication-defineddata.Typically,youcanuseTagto///installapointertoyourownapplication's associated data structure. Tag,///whichisinitializedto0atthetimeaTDocumentobjectisconstructed,isnot///otherwiseusedbythedocumentviewclasses.void*Tag;
Has public Get and Set methods
TView///Applicationhook,notusedinternally///Holdsapointertotheapplication-defineddata.Typically,youcanuseTagto///installapointertoyourownapplication's associated data structure. TView///zerosTagduringconstructionanddoesnotaccessitagain.void*Tag;
TGadget///IfClipisfalse,clippingbordershavenotbeenestablished.IfClipistrue,
///thedrawingforeachgadgetisrestrainedbythegadget's border. bool Clip;/// Initially set to false, WideAsPossible indicates whether the gadget width will/// be adjusted by the gadget window to be as wide as possible in the remaining/// space. bool WideAsPossible;
Clip - No accessors. It is not used by TGadget, but by TGadgetWindow instead,
when painting the gadget
WideAsPossible - Has public Get and Set methods. It is not used by TGadget,
but by TGadgetWindow instead, to count how many gadgets want to be wide as
possible
TGroupBox
/// Flag that indicates whether parent is to be notified when the state of the group
/// box's selection boxes has changed. NotifyParent is true by default.
bool NotifyParent;
All three are initialized by TInputDialog constructors and have public Get
methods. Changing them after construction makes no sense
TListViewintDirtyFlag; ///< flag to indicate if the view is dirty
VnIsDirty() returns DirtyFlag != 0. In some cases DirtyFlag is initialized
with true and false, but in others - with 2 (to detect our own close
notification). Should be hidden, and if needed provide SetDirtyFlag(bool)
which allows only true or false. The value 2 should be absolutely internal use
only.
TMDIClient
/// ClientAttr holds a pointer to a structure of the MDI client window's attributes.
LPCLIENTCREATESTRUCT ClientAttr;
Has Get and Set methods. The Set methods has comment: "Assumes ownership of
the pointer, so it will be deleted ? And the old one is orphaned and leaks?"
Initialized by constructor. Has public Get method. Set method makes no sense
TWindow/// Holds the handle to the associated MS-Windows window, which you'll need to/// access if you make calls directly to Windows API functions.HWNDHandle;/// Points to the window's caption. When there is a valid HWindow, Title will yield/// the same information as ::GetWindowText if you use TWindow::SetCaption to set/// it.LPTSTRTitle;// Logical title. Usually the same as window text/// Points to the interface object that serves as the parent window for this interface object.TWindow*Parent;// Owl parent, use GetParentO(), SetParent()/// Holds a TWindowAttr structure, which contains the window's creation attributes./// These attributes, which include the window's style, extended style, position,/// size, menu ID, child window ID, and menu accelerator table ID, are passed to the/// function that creates the window.TWindowAttrAttr;/// Holds the address of the default window procedure. DefWindowProc calls/// DefaultProc to process Windows messages that are not handled by the window.WNDPROCDefaultProc;/// Points to the scroller object that supports either the horizontal or vertical/// scrolling for this window.TScroller*Scroller;// Scrolling helper object
Handle - Has public Get method and protected Set which is to be used by
derived classes that create their window handle in a class-specific way.
Title - Has public GetCaption and SetCaption methods. SetCaption must be used
in order to set both the member and the actual window title
Parent - Has public Get and Set methods. SetParent() must be used in order to
keep the parent TWindow child list correct
Attr - Has public Get method. Used in window creation. Often some of it's
members are changes after the construction of the C++ object.
DefaultProc - Modified by the public SubclassWindowFunction(). Should not be
directly accessed.
Scroller - Has public Get method. Usualy instantiated by TWindow-derived
classes.
TXWindow
/// Points to the window object that is associated with the exception.
TWindow* Window;
Initialized by constructor. Has public Get method. Set method makes no sense
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Great summary, Jogy! I think you've already identified some instances of the
issues I feared.
TMDIClient::SetClientAttr seems like it has an obvious bug (lack of memory
management, as commented). Is there any need for an interface to replace the
struct? The struct can be modified through the non-const pointer returned by
GetClientAttr. If so, the return type of the latter should be a reference
rather than a pointer; although that is a breaking change, of course.
Does the struct need replacement? If not, a non-const getter is more
appropriate for mutation.
TDialogAttr
Agree, I see only sense in a Get method. I see no need to replace the struct.
If mutation of the struct is required; then add a non-const overload for Get
returning a non-const reference.
MainWindow - There are Get and Set methods. Cusrious thing is the the Set
method is protected.
SetMainWindow is designed to be used in a derived class; i.e. in
InitMainWindow. Could there be a need to set up the main window from outside
the class; i.e. should it be public?
TDocument::Tag, TView::Tag
Get and Set are ok, but on another note, I'd like these to be deprecated.
These void tags encourage C-style programming style. Derived classes,
polymorphism and/or templates provide safer and more flexible code.
TEditFile::FileName - Has public Get and Set methods. Also, it is better to
replace it with owl_string.
Agree, same goes for TInputDialog::Prompt and Buffer, as well as
TWindow::Title. These should use owl_string.
I agree on the non-sense setters; we should remove (i.e. put in compatibility
mode) or deprecate these.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hello,
There are a lot of OWLNext class members, which are declared as "public_data" or "protected_data", which are macros defined as follows:
I plan to try to enforce OWL_STRICT_DATA by default, except when building in OWL5.0 compatibility mode.
Also, there are some classes like TCmdLine, which are exposing their data members as public. I will hide them and add the necessary accessor methods.
Last edit: Vidar Hasfjord 2022-05-22
We should provide the getter/setter method first (in 6.30.x) and enforce the
use in next mayor release (6.40) with support for OWL5_COMPAT_MODE.
Sebas
Hello,
it seems to me there is no need to hide all data members. If no other data
values depend upon this one the declaration it as "protected" ot "private"
becomes "the strictness for the sake of strictness".
Thank you very much for 6.30.5 release.
The problem of writing and reading pointers to streamable objects under MSVS
2008 was fixed.
Igor
In the 6.31.04 development version the "public_data:" sections are changed to
be protected instead, unles building with OWL 5 compatibility.
Accessors are added and should be used instead of directly accessing the data
members.
Hi, I have a few reservations about this. There is a risk that we may reduce
the code quality, and actually introduce bugs, by providing accessors and
mutators indescriminately. Mutators in particular need special consideration.
I ran into a bug recently that was probably caused by the introduction of a
mutator; see "TPrinter::SetData leaks memory",
bug #3128107. In this case the mutator failed to manage ownership
(memory management). In general, a properly implemented mutator should always
uphold the class invariant (valid state).
This is of course also a problem with public access to internal data. But it
becomes worse when the class provides a broken interface to internal details.
Most programmers are wary of manipulating internal members, even, or
especially, if the members are public. The latter is a strong signal that the
class interface is poorly designed. On the other hand, a programmer expects
that a mutator interface is safe and upholds the class invariant, e.g.
TPrinter::SetData above.
There is also the question of the signature of accessors and mutators. For
example, TPrinter::GetData and SetData returns and receives a pointer. This is
most likely because the data is held internally by pointer. But this is an
implementation detail, and it makes no sense for GetData to return a null-
pointer or SetData to receive a null-pointer. TPrinter will always maintain
ownership of a TData instance in its valid state; i.e. a valid data member is
part of the class invariant. So there is a strong argument for the accessor
and mutator to use references instead.
Some members are truly internal implementation details and should have no
interface at all. There are probably many programs out there (heart surgeants)
that relies on access to truly internal details of OWL classes. For these we
should continue to provide a build option that provide public access; with the
warning that the innards may change and become private in the future, and that
we, for that reason, deprecate the build option. It makes no sense to provide
an interface (GetHeart and SetHeart) to these internal details. Even if the
accessors and mutators are implemented correctly they still violate the
encapsulation principle in good design.
So I suggest that public data members are categorized:
part of the class interface; provide access via accessors and mutators; the latter only if strictly needed, and if so, ensure the implementation upholds the class invariant.
truly internal; hide and don't add an interface. The programmer should either use a compatibility build option, or rewrite the code to not rely on internal details.
Regards,
Vidar Hasfjord
Hi, Vidar,
I agree. As a start, I created a list of the public_data members of most OWL
classes:
hPrevInstance - not used at all under 32-bit. There are Get and Set methods,
but the Set method makes no sense to be used.
nCmdShow - the value from WinMain() arguments. There are Get and Set methods,
but the Set method makes no sense to be used.
DocManager - There are Get and Set methods.
MainWindow - There are Get and Set methods. Cusrious thing is the the Set
method is protected.
LangId - There are Get and Set methods.
Tooltip - There are public Get and protected Set methods
Has public Get and Set methods.
Has public Get and Set methods.
Has public Get and Set methods. The Set methos also sends CB_LIMITTEXT to the
window, so definitely it should be used
Has public Get and Set methods. Initialized in the constructors and used
during dialog creation - so maybe only the Get method is needed.
Has public Get method
Has public Get and Set methods
Has public Get and Set methods
FileData - Has public Get and Set methods
FileName - Has public Get and Set methods. Also, it is better to replace it
with owl_string.
All have public Get and Set methods
Has public Get and Set methods
Clip - No accessors. It is not used by TGadget, but by TGadgetWindow instead,
when painting the gadget
WideAsPossible - Has public Get and Set methods. It is not used by TGadget,
but by TGadgetWindow instead, to count how many gadgets want to be wide as
possible
Has public Get and Set methods
All three are initialized by TInputDialog constructors and have public Get
methods. Changing them after construction makes no sense
VnIsDirty() returns DirtyFlag != 0. In some cases DirtyFlag is initialized
with true and false, but in others - with 2 (to detect our own close
notification). Should be hidden, and if needed provide SetDirtyFlag(bool)
which allows only true or false. The value 2 should be absolutely internal use
only.
Has Get and Set methods. The Set methods has comment: "Assumes ownership of
the pointer, so it will be deleted ? And the old one is orphaned and leaks?"
Both have public Get and Set methods.
Has public Get and Set methods
Initialized by constructor. Has public Get method. Set method makes no sense
Handle - Has public Get method and protected Set which is to be used by
derived classes that create their window handle in a class-specific way.
Title - Has public GetCaption and SetCaption methods. SetCaption must be used
in order to set both the member and the actual window title
Parent - Has public Get and Set methods. SetParent() must be used in order to
keep the parent TWindow child list correct
Attr - Has public Get method. Used in window creation. Often some of it's
members are changes after the construction of the C++ object.
DefaultProc - Modified by the public SubclassWindowFunction(). Should not be
directly accessed.
Scroller - Has public Get method. Usualy instantiated by TWindow-derived
classes.
Initialized by constructor. Has public Get method. Set method makes no sense
Great summary, Jogy! I think you've already identified some instances of the
issues I feared.
TMDIClient::SetClientAttr seems like it has an obvious bug (lack of memory
management, as commented). Is there any need for an interface to replace the
struct? The struct can be modified through the non-const pointer returned by
GetClientAttr. If so, the return type of the latter should be a reference
rather than a pointer; although that is a breaking change, of course.
Does the struct need replacement? If not, a non-const getter is more
appropriate for mutation.
Agree, I see only sense in a Get method. I see no need to replace the struct.
If mutation of the struct is required; then add a non-const overload for Get
returning a non-const reference.
SetMainWindow is designed to be used in a derived class; i.e. in
InitMainWindow. Could there be a need to set up the main window from outside
the class; i.e. should it be public?
Get and Set are ok, but on another note, I'd like these to be deprecated.
These void tags encourage C-style programming style. Derived classes,
polymorphism and/or templates provide safer and more flexible code.
Agree, same goes for TInputDialog::Prompt and Buffer, as well as
TWindow::Title. These should use owl_string.
I agree on the non-sense setters; we should remove (i.e. put in compatibility
mode) or deprecate these.