http://www.philweber.com/net/stories...eOverrated.htm
Printable View
"Phil Weber" <pweber@nospam.fawcette.com> wrote in message news:3de5eb1c$1@tnews.web.devx.com...
> http://www.philweber.com/net/stories...eOverrated.htm
That content class did not look right to me. You used a DB variable that was
not declared in the class. Consequently, I don't think you want your class to
be given access to the DB, rather Load and Save should only be accepting or
returning the xml string.
Your data access class might have ContentLoad and ContentSave methods that
you pass your content class(es) to and it calls the respective methods to
transfer the data.
I don't do that kind of work, I am just saying that it seems to me inheritance
should be reserved for when you have some significant behavior you need
copied, instead of multiple values. Could you not change your ID to work
more inline with what I suggested above (let the data access class assign it
when its loading the object from the DB) and make the whole thing a structure
that each of your classes use?
The part that troubled me is that you planned to have classes inherit from that
class, and (like the HomePageItem) they each would have a few of their own
variables. If the base class is saving the data, how do these extra variables get
saved?
LFS
> That content class did not look right to me.
Larry: Thanks for your comments. Trust me, it works. ;-) I copied and pasted
the code from a working application, omitting only the Option Explicit and
Option Strict lines, and the Imports statements.
> You used a DB variable that was not declared in the class.
GetContent and UpdateContent are static methods of the cmdDB (data access)
class. For more information, see
http://www.vbip.com/books/1861004915...er_4915_08.asp .
> I don't do that kind of work, I am just saying that it seems
> to me inheritance should be reserved for when you have some
> significant behavior you need copied, instead of multiple values.
I am inheriting the Load and Save methods from the base class, but other
than that, you're right, I'm just adding fields.
> Could you not change your ID to work more inline with what
> I suggested above (let the data access class assign it when
> it's loading the object from the DB) and make the whole thing
> a structure that each of your classes use?
I suppose I could, but I'm not sure what advantage that would have over the
solution I ended up with?
> The part that troubled me is that you planned to have classes
> inherit from that class, and (like the HomePageItem) they each
> would have a few of their own variables. If the base class is
> saving the data, how do these extra variables get saved?
The Save method uses XML serialization. The Framework uses reflection to get
information about each class' public members and creates an XML
representation automatically.
--
Phil Weber
"Phil Weber" <pweber@nospam.fawcette.com> wrote
> > That content class did not look right to me.
>
> Larry: Thanks for your comments. Trust me, it works. ;-) I copied and pasted
> the code from a working application, omitting only the Option Explicit and
> Option Strict lines, and the Imports statements.
That statement was a little vauge, I was not questioning the executable parts,
but the dependancy on a second object, which would make all inherited objects
also dependant on that same specifically named variable. That just does not
sit well with me. It seems to me, either such a reference should be passed in, or
it could be designed without the need for that object (which is what I mentioned).
This was the clarification of that first statement:
> > You used a DB variable that was not declared in the class.
>
> GetContent and UpdateContent are static methods of the cmdDB (data access)
> class. For more information, see
> http://www.vbip.com/books/1861004915...er_4915_08.asp .
I have that book and have glanced over most of it. ;-) (I am currently reading its
follow up edition, when I take time out for reading....)
> > Could you not change your ID to work more inline with what
> > I suggested ...
>
> I suppose I could, but I'm not sure what advantage that would have over the
> solution I ended up with?
You have probably picked a good solution for your needs, I was only suggesting
an alternate method that would drop that DB variable.
> The Save method uses XML serialization. The Framework uses reflection to get
> information about each class' public members and creates an XML
> representation automatically.
The use of Me in that class slipped by me. I still have a mindset that thinks
Me refers to the class that includes it. I wonder if it would have been better
to include a This reference, in those places that mean to reference a derived
class? I mean after all those years of Me being rather private, and personal,
and all... <g>
LFS
Phil Weber wrote:
>
http://www.philweber.com/net/stories...nceOverrated.h
tm
It's an interesting example, thanks for writing your experiences.
It doesn't seem that inheritance was the cause of any of the problems
you faced though. You could've just as easily created a bunch of
classes in VB6, delegating common functionality to a
"ContentItemDataService" Load/Save object, and be faced with exactly the
same problems (a class for each type of content, and type-specific XML
stores).
Your ContentItem base class stores the Type as a public String property,
which immediately indicates to me that you are not really "using"
inheritance (TypeOf is available if you must examine the type).
The problems seemed to be more based around the requirement for very
generic data. I get the impression that your data store is one table
keyed on ID, with a column containing a hashtable of any old name-value
strings (along with LiveDate, EditControl columns).
Say, for example, you refactored your data store into an RDBMS with a
"base" ContentItem table indexing all the ID's, and various HomePage,
FeaureArticle, ProductReview, etc. tables with a foreign-key relation to
the ContentItem table... would the problems you raised be any
different?
On the subject of creating a Content Management System, I think you are
in danger of creating a generic framework, rather than an easy to
understand (and therefore maintain) Fawcette Content System. If there
is no behaviour that is specific to Fawcette, then why are you writing
it? Chances are some 3rd-party has already gone to far more effort at
writing a generic CMS that you can customize using your XML form
definitions.
It only takes an hour to derive a few HomePageItem* classes, and
subsequently minutes to modify HomePageItem behaviour. But if I'm faced
with maintaining your generic CMS framework, then I would have to spend
weeks figuring out how it all works (you don't have time to write a
programmer's manual do you!). Rather than a self-documenting class
structure, I would have to route around tables looking at loads of
Strings, and probably end up hoping that your framework will treat my
new Strings as intended (not to mention spelling mistakes).
Strong-typing is more maintainable than weak-typing (dynamic vs. static
is another matter). Self-documenting is more maintainable than
manual-documenting.
--
David
"Phil Weber" <pweber@nospam.fawcette.com> wrote in message
news:3de5eb1c$1@tnews.web.devx.com...
> http://www.philweber.com/net/stories...eOverrated.htm
>
I agree with Larry in thinking that the 'thinking' involved in that solution
looks very wrong, and that global cmsDB thing is pretty lethal :)
One of the features I find most useful in inheritence is not so much that
'cat' and 'dog' are both types of 'animal', but more the fact that I can
define an 'animal' base, then a 'mammal' base, then a 'dog' base, with each
layer adding a small piece of extra functionality, making it much simpler to
understand, debug, and extend in the future. The fact that I can then look
at any of those levels, and decide (for example) that 'donkey' is a kind of
'mammal' is an added bonus, while it also makes it quite clear to me that
'goldfish' can only contain properties in 'animal' and not 'mammal' :)
Of course, I often discover that I have put something like 'size_of_brain'
into mammal, then I have to move it to 'animal', but class inheritence makes
that very easy and very quick, so as my project progresses my classes get
more specific.
Since inheritence is all about 'is a' relationships, my brief look at your
example suggests you are trying to force 'is a' situations onto the wrong
aspects of objects, rather like trying to relate 'fish' and 'dog' to an
interface with a method 'attach_collar' :)
OO stuff never works unless you can find the common aspects of your problem.
What common method could you add to every one of your 'content objects' that
would let it tell your editor how to display itself? 'Get_XML' doesn't
really work, because it only contains data, although you could add enough
information for an edit screen to format itself. This would work, and would
make your data both self-describing and self-edit-describing, but it's
messy, complex, and future changes to your editor could make earlier records
invalid.
A more sensible solution may be to implement your edit functionality in the
object itself, so your interface calls the object instance to return an
editor form or panel. You could inherit a standard form, user control, or
panel object, add all of the textbox, radio button, checkbox edit controls
etc. linked to the objects underlying data, and add a simple Interface to
add your CMS persistence functions. This would allow any part of your
application to bring up the data for that content for editing, and adding a
'read-only' property would allow you to use if for display only situations
too.
In this situation I have assumed that the common aspect is that you always
want to display all of the data fields in your object, and optionaly allow
editing, so I have moved the edit functionality to the class because it
appears to me that the class itself is the only thing that can decide how to
do this.
Alternative might be to return a collection of attributes and their types
from a common 'getAttributes' method, write a function that displays them in
a form based on each one's type, then implement a 'setAttributes' to write
them back after editing. A pretty awful suggestion, similar to the XML one
above, but one that moves the visual editing responsibility away from the
content object :)
I hope this makes sense. It's not a solution to your problem (although I
like the idea of the form thing. I'll try it myself soon :) ), but it is my
attempt at interpretation of how I often think about applying OO concepts to
a problem.
I stand by my comments several months ago (which I got flamed for) that
'true' OO thinking for VB6 (and similar language) converts is very very
difficult, making the transition to .NET difficult. I understand exactly why
they struggle, largely because the most important OO design concepts are
often not taught in any books, and the only reason I find it less than
difficult is because I've done (and taught) lots of Java and C++ code in the
past.
Having been a VB6 person for ten years, I found the .NET changeover
frustrating, and I put it off for many months. I'm now reasonably happy, and
while I still do much VB6 work I do prefer the .NET framework.
For those wanting to shortcut many of the thinking processes involved in
recognising these kind of patterns in their OO development, I strongly
recommend the book of Design Patterns by Gamma et al. as a reference.
http://hillside.net/patterns/DPBook/DPBook.html
There is also an article about them at this link:
http://www.codeproject.com/useritems/csdespat_1.asp
Unfortunately I can't get the page to appear, possibly because my ISP is
stuffing around with my connection :/
Cheers,
Jason
> It doesn't seem that inheritance was the cause of any
> of the problems you faced though.
David: OK, but I avoided the problems by moving to a solution that doesn't
use inheritance. I guess strong-typing was really the issue. In general, I'm
in favor of strong typing, but this case would require significant code for
each new type, and a lot of maintenance whenever a type changes. My generic
solution will save a LOT of time and effort.
> Your ContentItem base class stores the Type as a public
> String property, which immediately indicates to me that you
> are not really "using" inheritance (TypeOf is available if you
> must examine the type).
Perhaps "Type" isn't the best name for that field; I use it to indicate
which user control to use to display the content. I could call it
DisplayControl, would I be "using" inheritance then? ;-) Or I suppose I
could give the user control the same name as the associated content class,
then use GetType to retrieve the name. The problem with that is that
eventually our Web designers should be able to create the display controls;
they shouldn't have to know or care about my class names.
> The problems seemed to be more based around the requirement
> for very generic data. I get the impression that your data store is
> one table keyed on ID, with a column containing a hashtable of any
> old name-value strings (along with LiveDate, EditControl columns).
That's essentially correct. I'm using SQL Server to simulate an object
database, storing the content objects in a text column as XML.
> Say, for example, you refactored your data store into an
> RDBMS with a "base" ContentItem table indexing all the ID's,
> and various HomePage, FeaureArticle, ProductReview, etc.
> tables with a foreign-key relation to the ContentItem table...
> Would the problems you raised be any different?
Yes, they'd be different. Instead of being able to reuse a single Load and
Save method for all content classes, I'd have to write separate stored
procedures and data access code for each class, and maintain a separate
table for each content type. A LOT more work.
The goal here is to turn this project around as quickly as possible, and to
make it as low-maintenance as possible. I have a lot of other projects to
work on; FTP doesn't want me to have to touch the code every time we launch
a new site, or anytime an editor wants to make a change to a content type.
> On the subject of creating a Content Management System,
> I think you are in danger of creating a generic framework...
That's exactly what I'm doing. Why is that a danger? FTP wants a framework
that it can use with its existing family of sites, as well as any new sites
it may decide to launch in the future.
> If there is no behaviour that is specific to Fawcette,
> then why are you writing it?
I'm writing it because we determined that it would be less expensive and
better suited to our immediate needs to develop our own simple framework,
rather than pay the tens of thousands of dollars PER PROCESSOR charged by
commercial CMS vendors.
> Chances are some 3rd-party has already gone to far more
> effort at writing a generic CMS that you can customize using
> your XML form definitions.
If you know of such a product, please feel free to recommend it. :-)
> It only takes an hour to derive a few HomePageItem* classes,
> and subsequently minutes to modify HomePageItem behaviour.
That's an hour + minutes that I won't have to spend maintaining my generic
framework. The users can create a new content type without my involvement,
simply by creating a user control to display the content, an XML edit form
definition, and adding a row to a ContentTypes table.
--
Phil Weber
> I agree with Larry in thinking that the 'thinking' involved
> in that solution looks very wrong, and that global cmsDB
> thing is pretty lethal :)
Jason: What's the big deal about the cmsDB class? Isn't it common practice
to encapsulate data access code in a separate class? That's how I've always
done it. Would you have each content class access the database directly?
> Since inheritence is all about 'is a' relationships, my brief look
> at your example suggests you are trying to force 'is a' situations
> onto the wrong aspects of objects, rather like trying to relate
> 'fish' and 'dog' to an interface with a method 'attach_collar' :)
Sorry, I don't see it that way. A home page blurb 'is a' content item, an
article 'is a' content item. There we go: content item is our base class.
Now, a feature 'is an' article, a product review 'is an' article; features
and product reviews can inherit from article, which inherits from content
item. Seems like a perfectly sensible hierarchy to me. All content items
share certain properties, as well as the ability to load and save
themselves. All articles share certain additional properties (author, for
example). What am I missing?
This all worked fine, except that it's fairly maintenance-intensive. As I
mentioned in my reply to David, it's not acceptable to require that a
developer modify the code anytime a new content type is required.
So my question is: Is this a common trait of inheritance-based object models
(that a developer must modify the code anytime the underlying data
structures change)? If so, does nobody else see a problem with it? Doesn't
it make more sense in a case like this to have a flexible object model and a
data-driven UI, so that users can make the changes they need without the
involvement of a developer?
--
Phil Weber
"Phil Weber" <pweber@nospam.fawcette.com> wrote in message
news:3de894a5@tnews.web.devx.com...
> > I agree with Larry in thinking that the 'thinking' involved
> > in that solution looks very wrong, and that global cmsDB
> > thing is pretty lethal :)
>
> Jason: What's the big deal about the cmsDB class? Isn't it common practice
> to encapsulate data access code in a separate class? That's how I've
always
> done it. Would you have each content class access the database directly?
Yes, but it is _very_ bad practise to create a 'global' variable and
reference an object through it. In this case you reference an external cmsDB
object which is not programmed into your class. A 'proper' way to do this
would be to either have a 'setCmsDB' method, or pass the cmsDB in as a
parameter.
This sort of thing comes back to haunt you when (for example) you create an
instance of an objects that talks to another database, or when you create an
instance when the database has not been initialised. You would be better
telling the class 'Do this, using database X'.
Alternatively, since these classes are derived from a standard base class of
type 'Y', you should be able to say cmsDB.SaveData(myobj as Y), because you
are really asking the database to do something with your object, rather than
asking the object to go and do something to the database.
Does this make sense?
> > Since inheritence is all about 'is a' relationships, my brief look
> > at your example suggests you are trying to force 'is a' situations
> > onto the wrong aspects of objects, rather like trying to relate
> > 'fish' and 'dog' to an interface with a method 'attach_collar' :)
>
> Sorry, I don't see it that way. A home page blurb 'is a' content item, an
> article 'is a' content item. There we go: content item is our base class.
> Now, a feature 'is an' article, a product review 'is an' article; features
> and product reviews can inherit from article, which inherits from content
> item. Seems like a perfectly sensible hierarchy to me. All content items
> share certain properties, as well as the ability to load and save
> themselves. All articles share certain additional properties (author, for
> example). What am I missing?
Perhaps that wasn't a very good analogy, but I was trying to say that a dog
and a fish are animals, but that is a very abstract similarity. You might
also say that a Form and a ListBox are both visual elements, but they have
very different functionality and can't have exactly the same interfaces, so
unless you are doing incredibly simple functions with them, they have to be
treated in very different manners.
While these objects share the ability to load/save themselves, is this
specific to content items? I would say probably not, because you may wish to
allow all sorts of object to be saved/loaded. Wouldn't it be better to be
able to pass an object to dbCMS and ask it to write it for you? An Interface
assigned to any object that should be persistent would implement this
nicely. This is how the Serializable interface operates.
Your 'homepage' and 'review' both contain data, but those attributes are
completely different in their structure, presentation, and validation rules,
so they are really not particularly similar at all, and asking another
object to take either type as a parameter is going to be very messy and
inflexible to code.
e.g.
X = cmdDB.LoadObject(strMyPage)
If TypeOf(X is homepage)
Show a screen specifically for editing a homepage
ElseIf TypeOf(X is article)
Show a screen specifically for editing an article
etc.
You have to find a flexible way of getting around this, which is why I
suggested
X = cmdDB.LoadObject(strMyPage)
X.EditPage
This way, no matter how many new types of content you create, the
functionality in that class will never change.
If you find that a 'review' is almost identical to an 'article', then simply
subclass an 'article' and apply the changes, which is much simpler than
creating a new class from 'content'.
> This all worked fine, except that it's fairly maintenance-intensive. As I
> mentioned in my reply to David, it's not acceptable to require that a
> developer modify the code anytime a new content type is required.
But that is impossible in any language unless you include all of the editing
information with your data (as in my example re XML or collections of
attributes).
> So my question is: Is this a common trait of inheritance-based object
models
> (that a developer must modify the code anytime the underlying data
> structures change)? If so, does nobody else see a problem with it? Doesn't
> it make more sense in a case like this to have a flexible object model and
a
> data-driven UI, so that users can make the changes they need without the
> involvement of a developer?
In the ideal world, the interface would adapt to any type of data at all,
but in practise this doesn't work well at all. You've probably seen database
applications that automatically generate an edit form for a table? They are
awful, and unless you add dozens of attributes in an additional table or
config file they will usually miss simple requirements such as restricting a
range of values or visually representing foreign key joins in the way you
want.
This has nothing to do with OO development, and is a standard programming
situation. The beauty of a good OO implementation is that coding to add a
new 'content' should be minimal, not effect any other code, and should
inherit all of your previous hard work in the base classes.
Once you get the hang of it, this is what makes OO development so much
smoother and more satisfying than procedural code.
I'm not criticising your article or you, I just think I can see where you
are suggesting practices that are not ideal solutions from an OO
perspective, which I why I'm trying to suggest an alternative :)
Cheers,
Jason
"Phil Weber" <pweber@nospam.fawcette.com> wrote
> Sorry, I don't see it that way. A home page blurb 'is a' content item, an
> article 'is a' content item. There we go: content item is our base class.
> Now, a feature 'is an' article, a product review 'is an' article; features
> and product reviews can inherit from article, which inherits from content
> item. Seems like a perfectly sensible hierarchy to me. All content items
> share certain properties, as well as the ability to load and save
> themselves. All articles share certain additional properties (author, for
> example). What am I missing?
You are attempting to say that something done to an object, is
a property/method of that object. Load and Save are tasks that
your content item is submitted to because the content item does
not control the result of the operation. For example, you could
save your object to a DB, as you intend, or to a file, or send it
across the wire, etc. It does not matter to the content item what
becomes of its data, only that it knows how to expose it.
To break it out into simple terms, consider a textbox, or a string.
They have much in common with your content item in that they
have a few properties and contain data. The string contains text,
but does it have a Load or Save method to save it to a file?
The question you need to answer is, why doesn't it have a Load
or Save method? Consider the problems you found....
Except for those, I think you were on the right track with your
'perfectly sensible hierarchy'. A ContentItem could be a base class
for your CMS (Content Management System) with a few properties
like; UID, Class, Author, CreationDate, AccessLevel, and Binary
and/or XML data properties, to name a few. You can use that base
class to name all the properties and methods a content item must support,
even if you attach the MustOverride keyword to insure the derived class
implements the property or method before it can be used.
That might be enough to hold images, but large textual data normally
requires pagination. So, from that ContentItem, you derive a secondary
base class called PagedContentItem, and give it properties and methods
to dole out its data in measured pages when needed.
For short items, like author bios, any short texts or images (banners/ads),
you'd inherit from the ContentItem, but for larger articles you would
inherit from your PagedContentItem class.
In that respect, there is little difference between a featured article, or
a review, unless you have some other details that were not mentioned
above. Similarly, you might create a separate class that is a mutli-item
class so that you can maintain a tight relationship between (for example)
an article and its listings, or figures. That way, when you send your multi-
item object to the DB class to get a specific multi-item object, you get back
all the objects needed for display on that page, instead of needing separate
calls for the related items.
Come to think if it, your PagedContentItem might be a special implementation
of the MultiItem class. I don't know tho, I'd have to think about that....
In any case, good luck! There must be a reason they get the big bucks
for such a packaged system!
<g>
LFS
> To break it out into simple terms, consider a textbox, or a string.
> They have much in common with your content item in that they
> have a few properties and contain data. The string contains text,
> but does it have a Load or Save method to save it to a file?
Larry: A RichTextBox does; so does a PictureBox. ;-) So how does one decide?
--
Phil Weber
"Phil Weber" <pweber@nospam.fawcette.com> wrote
> > To break it out into simple terms, consider a textbox, or a string.
> > They have much in common with your content item in that they
> > have a few properties and contain data. The string contains text,
> > but does it have a Load or Save method to save it to a file?
>
> Larry: A RichTextBox does; so does a PictureBox. ;-)
> So how does one decide?
I think the RTB was someone's kitchen sink idea. MS may have
had a reason to take the user out of the process. I am not party
to their design decisions, but you gotta admit, it is an exception.
What property of the picturebox do you use to load a file?
I couldn't see it. The normal method (AFAIK) is to use the
Image class: PictureBox1.Image = Image.FromFile(path)
LFS
> What property of the picturebox do you use to load a file?
Larry: I was thinking of the "Classic" VB PictureBox, which has LoadPicture
and SavePicture methods.
--
Phil Weber
"Phil Weber" <pweber@nospam.fawcette.com> wrote
> > What property of the picturebox do you use to load a file?
>
> Larry: I was thinking of the "Classic" VB PictureBox, which has LoadPicture
> and SavePicture methods.
Really? Can you show an example that works? I don't know about VB6,
but according to MSDN and in every version I've used, LoadPicture and
SavePicture were standard functions of the language, not methods on a
picturebox.
As I said, the RTB is an exception, for whatever reason. To futher make
the point that objects only expose their data, consider the new feature on
the ArrayLists and Listboxes that allow adding a list of items. It is only
natural that objects that are collections provide a means to add in other
collections, and you'll note that AddRange is supported in lots of other
areas. It is almost a no-brainer that such a method is needed, when you
give it a moment's thought, and MS did not hesitate to include it during
the major overhaul.
That is how display objects should also respond. Either a whole form
or a control (part of the form) should accept its data in a standardised
fashion such that the data knows nothing about where it is going, and
the form or control knows nothing about where the data is coming from.
All that is known is the interface by which the transfer is made.
That would mean then, your form(or control) that displays a content
item, should simply have something like an AddItem method that
accepts a content item. It may also support an AddRange for a
collection of content items, where the form has code to place and
display the separate items. Much like you expect a Listbox to respond.
I think if you look into what the lower level .Net objects provide, and
how they interact, you may get a feel for what is needed for your own
low level objects, and how they should interact. When you're describing
your content item base class, you are not working on a complex item
like a TreeView or other such rich object, you are working at a basic
level and need to restrict your ideas to their most basic needs.
LFS
"Larry Serflaten" <serflaten@usinternet.com> wrote
>
> As I said, the RTB is an exception, for whatever reason. To futher make
> the point that objects only expose their data,
That should be qualified to 'that stateful objects...' Such as your content item
would be....
LFS