423,851 Members | 2,680 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 423,851 IT Pros & Developers. It's quick & easy.

Try #2 - Inheritance work-around in VBA

P: n/a
Recently, I tried and did a poor job explaining an idea I've had for handling
a particular case of implementation inheritance that would be easy and obvious
in a fully OOP language, but is not at all obvious in VBA which lacks
inheritance. I'm trying the explanation again now.

I often find cases where a limited form of inheritance would eliminate
duplication my code that seems impossible to eliminate otherwise. I'm getting
very rigorous, now, about duplication because a seroius source of bugs when
code is modified later on. After you try being rigorous about it, then try to
work on code that's not written that way, you'll quickly see what I mean.

So, the specific case of inheritance I'm talking about is a base class that
contains the majority of the code and interfaces needed for an operation, but
there needs to be several minor variations on that operation, mainly
comprising attributes and functionality internal to the operation, not exposed
to external code.

Say I have an application in which the code frequently needs to retrieve the
ID of an item in a lookup table given the name, and automatically insert the
item first if it doesn't exist. Now, say I need to do this same operation for
many different tables at different times. Let's also assume that, in this
application, each table has a hard meaning known to specific parts of the
code. We are not addressing working with collections of types abstractly in
loops, etc. (that would be another layer with another solution).

If we had inheritance capability in the language, the design pattern would be
obvious. The base class (the Abstract class) contains the logic, and an
inheriting class (the Concrete class) for each lookup table provides values to
the base class for table name, name field, and ID field. The Abstract class
provides virtual (not implemented in the base class) private members for the
Concrete elements, and each Concrete class provides the implementations of
those, so the code in the Abstract class can access them.

The way inheritance is normally simulated in languages like VBA is that the
Concrete class encapsulates (contains a reference to) the Abstract class, and
provides external access to the Abstract class' members via a wrapper method
for each. For the situation I've described, that's a total mess, which is why
this case is such a prolem and in need of this solution; it adds as much
duplication as it removes. Every Concrete class has to implement and expose
all of its concrete members (that external code doesn't need) plus a wrapper
for every single public member of the Abstract class (that the Concrete class
doesn't implement).

My solution is to invert the encapsulation, so that the "Abstract" class
encapsulates the "Concrete" class. The external code interacts with the
"Abstract" class directly, and that class invokes the "Concrete" class to
obtain the specifics for the case at hand.

Working code example follows. Note that the concrete class could provide
processes (such as special initialization) as well as static properties.
-- basTests

Option Compare Database
Option Explicit

Public Function GetNewLookupValueList( _
objValueListConcrete As IValueListConcrete _
) As clsLookupValueList
Dim objLookupValueList As New clsLookupValueList
objLookupValueList.Setup objValueListConcrete
Set GetNewLookupValueList = objLookupValueList
End Function

Sub RunTests()
With GetNewLookupValueList(New clsLookupValListColor)
Debug.Print .IdFromName("Blue")
End With

With GetNewLookupValueList(New clsLookupValListStyle)
Debug.Print .IdFromName("Gaudy")
End With
End Sub
-- clsLookupValueList --

Option Compare Database
Option Explicit
Private mobjConcrete As IValueListConcrete
Private mdbs As DAO.Database
Private mrst As DAO.Recordset

Public Sub Setup( _
objValueListConcrete As IValueListConcrete _
)
Set mobjConcrete = objValueListConcrete
End Sub

Private Property Get SelectSql() As String
With mobjConcrete
SelectSql = _
"SELECT " & _
.FieldNameId & ", " & _
.FieldNameItem & " " & _
"FROM " & _
.TableName
End With
End Property

Private Function GetRecordset() As DAO.Recordset
If mrst Is Nothing Then
Set mdbs = CurrentDb()
Set mrst = mdbs.OpenRecordset(SelectSql)
End If
Set GetRecordset = mrst
End Function

Private Property Get IdField() As DAO.Field
' Could be made public, but not needed now, so not exposed.
Set IdField = GetRecordset.Fields(mobjConcrete.FieldNameId)
End Property

Private Property Get ItemField() As DAO.Field
' Could be made public, but not needed now, so not exposed.
Set ItemField = GetRecordset.Fields(mobjConcrete.FieldNameItem)
End Property

Public Function IdFromName(strName As String) As Long
Dim varResult As Variant
varResult = Null
With GetRecordset()
If .RecordCount > 0 Then
.FindFirst _
mobjConcrete.FieldNameItem & "=" & _
StringToSqlStringExpr(strName)
If Not .NoMatch Then
varResult = IdField.Value
End If
End If
If IsNull(varResult) Then
.AddNew
ItemField.Value = strName
.Update
.Bookmark = .LastModified
varResult = IdField.Value
End If
End With
IdFromName = varResult
End Function

Private Function StringToSqlStringExpr(strString As String) As String
StringToSqlStringExpr = "'" & Replace(strString, "'", "''") & "'"
End Function

Public Function NameFromID(lngID As Long) As Variant
Dim varResult As Variant
varResult = Null
With GetRecordset()
If .RecordCount > 0 Then
.FindFirst mobjConcrete.FieldNameId & "=" & lngID
If Not .NoMatch Then
varResult = IdField.Value
End If
End If
End With
NameFromID = varResult
End Function

Private Sub Class_Terminate()
If Not (mrst Is Nothing) Then
mrst.Close
Set mrst = Nothing
End If
Set mdbs = Nothing
End Sub
-- IValueListConcrete --

Option Compare Database
Option Explicit

Public Property Get TableName() As String
End Property

Public Property Get FieldNameId() As String
End Property

Public Property Get FieldNameItem() As String
End Property
-- clsLookupValListColor --

Option Compare Database
Option Explicit

Implements IValueListConcrete

Private Property Get IValueListConcrete_TableName() _
As String
IValueListConcrete_TableName = "tblColor"
End Property

Private Property Get IValueListConcrete_FieldNameId() _
As String
IValueListConcrete_FieldNameId = "ColorID"
End Property

Private Property Get IValueListConcrete_FieldNameItem() _
As String
IValueListConcrete_FieldNameItem = "Color"
End Property
-- clsLookupValListStyle --

Option Compare Database
Option Explicit

Implements IValueListConcrete

Private Property Get IValueListConcrete_TableName() _
As String
IValueListConcrete_TableName = "tblStyle"
End Property

Private Property Get IValueListConcrete_FieldNameId() _
As String
IValueListConcrete_FieldNameId = "StyleID"
End Property

Private Property Get IValueListConcrete_FieldNameItem() _
As String
IValueListConcrete_FieldNameItem = "Style"
End Property

Nov 12 '05 #1
Share this Question
Share on Google+
14 Replies


P: n/a
On Tue, 13 Apr 2004 07:20:06 GMT, Steve Jorgensen <no****@nospam.nospam>
wrote:

....
Correction
The way inheritance is normally simulated in languages like VBA is that the
Concrete class encapsulates (contains a reference to) the Abstract class, and
provides external access to the Abstract class' members via a wrapper method
for each. For the situation I've described, that's a total mess, which is why
this case is such a prolem and in need of this solution; it adds as much
duplication as it removes. Every Concrete class has to implement and expose
all of its concrete members (that external code doesn't need) plus a wrapper
for every single public member of the Abstract class (that the Concrete class
doesn't implement).


My intention was to say it is the standard solution that adds as much
duplication as it removes, not that my proposed solution adds as much
duplication as it removes <g>.
Nov 12 '05 #2

P: n/a
Steve Jorgensen <no****@nospam.nospam> wrote in
news:54********************************@4ax.com:
On Tue, 13 Apr 2004 07:20:06 GMT, Steve Jorgensen
<no****@nospam.nospam> wrote:

...
Correction
The way inheritance is normally simulated in languages like VBA is
that the Concrete class encapsulates (contains a reference to) the
Abstract class, and provides external access to the Abstract
class' members via a wrapper method for each. For the situation
I've described, that's a total mess, which is why this case is
such a prolem and in need of this solution; it adds as much
duplication as it removes. Every Concrete class has to implement
and expose all of its concrete members (that external code doesn't
need) plus a wrapper for every single public member of the
Abstract class (that the Concrete class doesn't implement).


My intention was to say it is the standard solution that adds as
much duplication as it removes, not that my proposed solution adds
as much duplication as it removes <g>.


I'm beginning to get it, but I'm still bogged down in your example
implementation. The task you've set for yourself seems really simple
to me -- it's the kind of thing I do all the time by various
methods, but it's really a very elaborate DLookup(), but with the
added feature that it adds the value if it's not there already?

I'm not sure what value you've gotten from this complexity. How many
times do you really have to do this kind of thing? Assuming a
structure for all your tables that you're working with of ID field
and value field (variant?), then I just don't see where the code
duplication would come in. Working up from the simplest case, I end
up with the code after my signature.

Now, that could easily be generalized to returning any field based
on any criteria (via a parameter array), assuming you are returning
one field. It wouldn't produce horrid code, in my opinion, but you
would end up with a function overloaded with arguments that would
have to be passed in a very special format. That always leads me in
the direction of a class module, since that can document much more
easily what the requirements are for getting accurate data out of
it.

But I still don't see where there needs to be a base class and then
specific classes.

I very, very often write simply one-line wrapper functions around
complex class modules (most often when I need to use the output in a
query, which can't refer directly to class modules), and your
example concrete classes have only a very small number of
properties/methods/members.

So, I'm just not seeing where the benefit is here:

1. your example seems more easily implemented in a much more
straightforward manner

2. I'm not able to think of an example that isn't likewise going to
be simple like that.

Each time you choose an example, you pick table interaction as your
subject. Table interactions are, I think, pretty easy to deal with.
Indeed, I kept thinking while reading your code that I sometimes use
tables to map relationships between tables and data within those
tables (tables as storage structures that themselves drive the
behavior of code; that is, tables that don't store data about the
app's end-user viewed data, but that store information that is used
entirely to drive code operation). The most common example of this
that I'm sure many people have used is when importing/exporting data
you often use a table to map fields on one side to fields on the
other.

Now, where I get a glimmer of potential for this kind of thing is
with objects that are not generic (like tables), but that are
specific to a particular application, but that are used in multiple
ways. We have a certain amount of "pass-through" encapsulation
available to us if we, say, wrap a class module around a generic
form. We can expose the properties, methods and members of the
encapsulated form directly through a form-type property of the
encapsulating class (including custom properties/methods/members of
the encapsulated form), while also enhancing the encapasulated form
with additional propertis/methods/members.

It's not real inheritance, but it's exactly what you've described,
in that it's upside-down, placing the "abstract" class as a wrapper
around the "concrete" class (in this case, a form).

Now, you can do the same kind of thing with a class module, as
you've shown, in that one of the properties of the "abstract" class
returns a reference to the "concrete" class.

So, it sort of makes sense to me from a structural level, but your
specific examples are sort of sabotaging my ability to understand it
clearly.

And when described the way I've done above, it doesn't sound so
earth-shattering. But when you talk about using it with pure class
modules (not forms), it does seem to me to have value.

The key breakthrough for me here was thinking about the idea of
wanting to add a bunch of behaviors and properties to a lot of
objects of exactly the same type. When I looked at the concept of a
wrapper around a bunch of forms that added new capabilities to all
the forms thus encapsulated, it began to make sense.

But I think it's a mistake to talk about it as though it's
inheritance. It really isn't at all -- it's just a form of
encapsulation that gives you the same benefits in regard to
eliminating code duplication as pure inheritance does.

(Of course, I'm also assuming that I've finally understood!)

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc

Public Function idLookup(strLookupValue As String, _
strLookupField As String, strLookupTable As String, _
Optional ysnAdd As Boolean = True, _
Optional db As DAO.Database) As Long
Dim ysnReleaseDB As Boolean
Dim strSQL As String
Dim rs As DAO.Recordset
Dim strIDField As String

If db Is Nothing Then
Set db = CurrentDb()
ysnReleaseDB = True
End If
strIDField = Mid(strLookupTable, 4) & "ID"
strSQL = "SELECT " & strLookupTable & "." & strIDField
strSQL = strSQL & ", " & strLookupTable & "." & strLookupField
strSQL = strSQL & " FROM " & strLookupTable
strSQL = strSQL & " WHERE " & strLookupTable & "."
strSQL = strSQL & strLookupField & "='"
strSQL = strSQL & strLookupValue & "';"
Set rs = db.OpenRecordset(strSQL)
If rs.RecordCount = 0 Then
If ysnAdd Then
rs.AddNew
rs(strLookupField) = strLookupValue
rs.Update
End If
End If
If rs.RecordCount <> 0 Then
rs.MoveFirst
idLookup = rs(strIDField)
End If

rs.Close
Set rs = Nothing
If ysnReleaseDB Then Set db = Nothing
End Function
Nov 12 '05 #3

P: n/a
rkc

"David W. Fenton" <dX********@bway.net.invalid> wrote in message
news:Xn**********************************@24.168.1 28.78...
Steve Jorgensen <no****@nospam.nospam> wrote in
news:54********************************@4ax.com: But I think it's a mistake to talk about it as though it's
inheritance. It really isn't at all -- it's just a form of
encapsulation that gives you the same benefits in regard to
eliminating code duplication as pure inheritance does.

(Of course, I'm also assuming that I've finally understood!)


It's containment. Nothing more. Nothing less.
One object instantiates another object as a member variable and
uses that objects properties and methods internally. It's containment.
If the member object's properties and methods were exposed to
outside code it would be delegation. Delegation is what containment
avoids. Exposing the member objects properties and methods to
outside code usually involves adding properties and methods to the
containing class. That is what Steve sees as code duplication. It's
not. It's just the way it's done.

This msdn article shows a way to sort of/kind of extend interface
classes to do much the same thing. Once again it's a long way
around the barn for people who don't live and breath oop.

http://msdn.microsoft.com/library/de...lymorphism.asp





Nov 12 '05 #4

P: n/a
On Tue, 13 Apr 2004 15:21:28 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:54********************************@4ax.com :
On Tue, 13 Apr 2004 07:20:06 GMT, Steve Jorgensen
<no****@nospam.nospam> wrote:

...
Correction
The way inheritance is normally simulated in languages like VBA is
that the Concrete class encapsulates (contains a reference to) the
Abstract class, and provides external access to the Abstract
class' members via a wrapper method for each. For the situation
I've described, that's a total mess, which is why this case is
such a prolem and in need of this solution; it adds as much
duplication as it removes. Every Concrete class has to implement
and expose all of its concrete members (that external code doesn't
need) plus a wrapper for every single public member of the
Abstract class (that the Concrete class doesn't implement).
My intention was to say it is the standard solution that adds as
much duplication as it removes, not that my proposed solution adds
as much duplication as it removes <g>.


I'm beginning to get it, but I'm still bogged down in your example
implementation. The task you've set for yourself seems really simple
to me -- it's the kind of thing I do all the time by various
methods, but it's really a very elaborate DLookup(), but with the
added feature that it adds the value if it's not there already?


Again, this is a pattern to be used for removing duplication within a
sepecific application. This is not intended to be general-purpose, it's an
example of how the pattern might be used in a single app.

I chose a simple example on purpose, though not as simple as my first case.
Although what the class does is really just a DLookup, the code that uses the
class should be not have to know or care that that's what the implementation
is doing. That code should not need to know field names or table names, or
how to build criteria strings. In fact, if external code is aware of these
things and does them more than once, it is duplicating code, and what's being
duplicated is not something the compiler can check for you, so there is the
possibility of typos, cut/paste errors, and Search/Replace errors.
I'm not sure what value you've gotten from this complexity. How many
times do you really have to do this kind of thing? Assuming a
structure for all your tables that you're working with of ID field
and value field (variant?), then I just don't see where the code
duplication would come in. Working up from the simplest case, I end
up with the code after my signature.
The duplication is not in the lookup code, the duplication is in the
application code that uses it. That's why what I'm talking about is a design
pattern to be applied in a given application, not a library routine to be
reused across applications. Using your code in an application, then using
that code multiple times for tables with the same nature (lookup table), and
use it multiple times for the same table as well, the calling code has
duplication. Of course, calling your code from my example would very likely
be desirable to remove duplication in another direction presuming one did not
also want to cache the recordset which may or may not make sense in a given
app, but it's better that this also be something the calling code does not
need to know. If I want to cache or not cache my lookup table recordsets,
there should only be one place to make the change.

Let's say I end up making this a client/server app on a busy network, and I
want to use disconnected recordsets for lookup tables. With the code
following the pattern I've outlined, only the code in the "Abstract" class has
to change. Neither the calling code nor the Concrete classes has to change.
Now, that could easily be generalized to returning any field based
on any criteria (via a parameter array), assuming you are returning
one field. It wouldn't produce horrid code, in my opinion, but you
would end up with a function overloaded with arguments that would
have to be passed in a very special format. That always leads me in
the direction of a class module, since that can document much more
easily what the requirements are for getting accurate data out of
it.

But I still don't see where there needs to be a base class and then
specific classes.
The specific classes encapsulate combinations of attributes (and custom
behaviors if/when they are added later) and keep each combination in one and
only one place in the code. That's the sole reason for their existence
I very, very often write simply one-line wrapper functions around
complex class modules (most often when I need to use the output in a
query, which can't refer directly to class modules), and your
example concrete classes have only a very small number of
properties/methods/members.

So, I'm just not seeing where the benefit is here:

1. your example seems more easily implemented in a much more
straightforward manner

2. I'm not able to think of an example that isn't likewise going to
be simple like that.

Each time you choose an example, you pick table interaction as your
subject. Table interactions are, I think, pretty easy to deal with.
Indeed, I kept thinking while reading your code that I sometimes use
tables to map relationships between tables and data within those
tables (tables as storage structures that themselves drive the
behavior of code; that is, tables that don't store data about the
app's end-user viewed data, but that store information that is used
entirely to drive code operation). The most common example of this
that I'm sure many people have used is when importing/exporting data
you often use a table to map fields on one side to fields on the
other.
A table makes sense when each case doesn't need to have a concrete identity
within the app, but needs to be flexible and data driven. In my case,
compile-time checking is a value. Also, in my real-world case, I have
encapsulated some behavior as well as data, and you can't do that with a
table. My code uses Concrete implementations of data sources, and the test
source gets automatically re-copied before it is opened. The example I have
shown here could and likely would evolve to have some encapsulated behavior
such as a table data integrity checker or some such (probably not that in
particular, but it's one real possibility).
Now, where I get a glimmer of potential for this kind of thing is
with objects that are not generic (like tables), but that are
specific to a particular application, but that are used in multiple
ways. We have a certain amount of "pass-through" encapsulation
available to us if we, say, wrap a class module around a generic
form. We can expose the properties, methods and members of the
encapsulated form directly through a form-type property of the
encapsulating class (including custom properties/methods/members of
the encapsulated form), while also enhancing the encapasulated form
with additional propertis/methods/members.
Well, my examples deal with tables primarily because I'm a database programmer
and this is an Access group. Yes, this design pattern would apply equally
well to many other kinds of things. It applies well to database interactions
because it keeps everything that needs to know it even -is- a database
interaction in one place and it keeps all the details about each specific use
case in a single place in the code.
It's not real inheritance, but it's exactly what you've described,
in that it's upside-down, placing the "abstract" class as a wrapper
around the "concrete" class (in this case, a form).
Your application has a much different feel because each "Concrete" class is
much heavier, but I agree that what you are talking about is probably the
exact same design pattern I'm talking about.
Now, you can do the same kind of thing with a class module, as
you've shown, in that one of the properties of the "abstract" class
returns a reference to the "concrete" class.

So, it sort of makes sense to me from a structural level, but your
specific examples are sort of sabotaging my ability to understand it
clearly.
Perhaps, what I've said above will change your mind. It's good to tuck the
code that knows how a lookup is implemented (including the fact that it's a
database table at all) away from the code that wants to look up a value.
And when described the way I've done above, it doesn't sound so
earth-shattering. But when you talk about using it with pure class
modules (not forms), it does seem to me to have value.

The key breakthrough for me here was thinking about the idea of
wanting to add a bunch of behaviors and properties to a lot of
objects of exactly the same type. When I looked at the concept of a
wrapper around a bunch of forms that added new capabilities to all
the forms thus encapsulated, it began to make sense.
Neat. I came up with the same pattern from a totally different direction. I
wanted to factor out just what was different from a group of table interaction
classes that had only a few differences without having a bunch of classes
wrapping a bunch of members of a single root class.
But I think it's a mistake to talk about it as though it's
inheritance. It really isn't at all -- it's just a form of
encapsulation that gives you the same benefits in regard to
eliminating code duplication as pure inheritance does.

(Of course, I'm also assuming that I've finally understood!)


I think you get it. If there's a better terminology for what I've come up
with, I'm interested in hearing it. Where I'm coming from is that I started
from a problem I knew very well how to handle if I did have inheritance as a
tool, and found an OK way to approximate it without using inheritance. So, to
me, the code class correlates with the Abstract class, and the detail classes
correlate with Concrete classes because that's how they actually would be
implemented if we were doing true OOP.
Nov 12 '05 #5

P: n/a
rkc

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:ug********************************@4ax.com...
The duplication is not in the lookup code, the duplication is in the
application code that uses it. That's why what I'm talking about is a design pattern to be applied in a given application, not a library routine to be
reused across applications.


So the calling code is where the less duplication part comes in. Was that
function in your first example? It's gone from my news server. It wasn't
in your second example.

Your latest example does make more clear to me what you are calling
a design pattern. I think though that the pattern is just the way you have
to do oop stuff using VB.


Nov 12 '05 #6

P: n/a
Steve Jorgensen <no****@nospam.nospam> wrote in
news:ug********************************@4ax.com:
On Tue, 13 Apr 2004 15:21:28 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:54********************************@4ax.co m:
On Tue, 13 Apr 2004 07:20:06 GMT, Steve Jorgensen
<no****@nospam.nospam> wrote:

...
Correction
The way inheritance is normally simulated in languages like VBA
is that the Concrete class encapsulates (contains a reference
to) the Abstract class, and provides external access to the
Abstract class' members via a wrapper method for each. For the
situation I've described, that's a total mess, which is why this
case is such a prolem and in need of this solution; it adds as
much duplication as it removes. Every Concrete class has to
implement and expose all of its concrete members (that external
code doesn't need) plus a wrapper for every single public member
of the Abstract class (that the Concrete class doesn't
implement).

My intention was to say it is the standard solution that adds as
much duplication as it removes, not that my proposed solution
adds as much duplication as it removes <g>.
I'm beginning to get it, but I'm still bogged down in your example
implementation. The task you've set for yourself seems really
simple to me -- it's the kind of thing I do all the time by
various methods, but it's really a very elaborate DLookup(), but
with the added feature that it adds the value if it's not there
already?


Again, this is a pattern to be used for removing duplication
within a sepecific application. This is not intended to be
general-purpose, it's an example of how the pattern might be used
in a single app.

I chose a simple example on purpose, though not as simple as my
first case. Although what the class does is really just a DLookup,
the code that uses the class should be not have to know or care
that that's what the implementation is doing. That code should
not need to know field names or table names, or how to build
criteria strings. In fact, if external code is aware of these
things and does them more than once, it is duplicating code, and
what's being duplicated is not something the compiler can check
for you, so there is the possibility of typos, cut/paste errors,
and Search/Replace errors.


I don't see how my example would lead to duplication of any kind in
this regard.
I'm not sure what value you've gotten from this complexity. How
many times do you really have to do this kind of thing? Assuming a
structure for all your tables that you're working with of ID field
and value field (variant?), then I just don't see where the code
duplication would come in. Working up from the simplest case, I
end up with the code after my signature.


The duplication is not in the lookup code, the duplication is in
the application code that uses it. That's why what I'm talking
about is a design pattern to be applied in a given application,
not a library routine to be reused across applications. Using
your code in an application, then using that code multiple times
for tables with the same nature (lookup table), and use it
multiple times for the same table as well, the calling code has
duplication. . . .


What?

You're saying that:

Dim lngID as Long

lngID = idLookup("Benzene", "Gas", "tblGas", True)

is *code duplication*?

My code can be used on any table of any kind that has these
characteristics:

1. has a unique ID PK

2. that ID field has a name derivable from the table name
("tblYourTable" -> "YourTableID")

3. has a second field with a non-duplicated text value.

Every table that has this characteristic could use this code.

If I wanted to extend it to multiple field lookup, I could have a
parameter array called in pairs of VALUE/FIELD.

I don't see that there's any code duplication caused by my proposal
of a short function to do this.

The most trivial problem with it is that your tables may have
differnt naming conventions for the ID field, so you'd work around
that some way.

But I don't see how my proposed solution increases code duplication
at all.

But it isn't infinitely extensible, no, nor does it validate too
many things.

I'm just not getting it, Steve.
. . . Of course, calling your code from my example would
very likely be desirable to remove duplication in another
direction presuming one did not also want to cache the recordset
which may or may not make sense in a given app, but it's better
that this also be something the calling code does not need to
know. If I want to cache or not cache my lookup table recordsets,
there should only be one place to make the change.
I'd think that lookup tables would be small enough that (and
probably stored locally) so that there's little reason to cache
them.
Let's say I end up making this a client/server app on a busy
network, and I want to use disconnected recordsets for lookup
tables. With the code following the pattern I've outlined, only
the code in the "Abstract" class has to change. Neither the
calling code nor the Concrete classes has to change.
I only have to change my code in one place, to, within the function,
idLookup().

I just don't get it, Steve.
Now, that could easily be generalized to returning any field based
on any criteria (via a parameter array), assuming you are
returning one field. It wouldn't produce horrid code, in my
opinion, but you would end up with a function overloaded with
arguments that would have to be passed in a very special format.
That always leads me in the direction of a class module, since
that can document much more easily what the requirements are for
getting accurate data out of it.

But I still don't see where there needs to be a base class and
then specific classes.


The specific classes encapsulate combinations of attributes (and
custom behaviors if/when they are added later) and keep each
combination in one and only one place in the code. That's the
sole reason for their existence


And how is it different than converting my simple function to a
single class?

I don't see why you need either encapsulation or inheritance.

I see only one level here.
I very, very often write simply one-line wrapper functions around
complex class modules (most often when I need to use the output in
a query, which can't refer directly to class modules), and your
example concrete classes have only a very small number of
properties/methods/members.

So, I'm just not seeing where the benefit is here:

1. your example seems more easily implemented in a much more
straightforward manner

2. I'm not able to think of an example that isn't likewise going
to be simple like that.

Each time you choose an example, you pick table interaction as
your subject. Table interactions are, I think, pretty easy to deal
with. Indeed, I kept thinking while reading your code that I
sometimes use tables to map relationships between tables and data
within those tables (tables as storage structures that themselves
drive the behavior of code; that is, tables that don't store data
about the app's end-user viewed data, but that store information
that is used entirely to drive code operation). The most common
example of this that I'm sure many people have used is when
importing/exporting data you often use a table to map fields on
one side to fields on the other.


A table makes sense when each case doesn't need to have a concrete
identity within the app, but needs to be flexible and data driven.


Yes, certainly. But I think you're depending on code for constructin
code structures that don't really *need* to be code structures. I
know your goal is having them be enforced by the compiler, but I'm
not sure that the value of that is so great as to justify the
enormous amount of extra code.
In my case, compile-time checking is a value. Also, in my
real-world case, I have encapsulated some behavior as well as
data, and you can't do that with a table. . . .
I understand that. All I'm saying is that tables can model
hierarchical relationships that aren't possible with the code
structures you have.
. . . My code uses Concrete
implementations of data sources, and the test source gets
automatically re-copied before it is opened. The example I have
shown here could and likely would evolve to have some encapsulated
behavior such as a table data integrity checker or some such
(probably not that in particular, but it's one real possibility).
I'm still foggy on why you need to do this the way you're doing it,
mostly because you've outlined the problem and not gone into detail.
I'm not sure the details would be any more enlightening, actually.
Now, where I get a glimmer of potential for this kind of thing is
with objects that are not generic (like tables), but that are
specific to a particular application, but that are used in
multiple ways. We have a certain amount of "pass-through"
encapsulation available to us if we, say, wrap a class module
around a generic form. We can expose the properties, methods and
members of the encapsulated form directly through a form-type
property of the encapsulating class (including custom
properties/methods/members of the encapsulated form), while also
enhancing the encapasulated form with additional
propertis/methods/members.


Well, my examples deal with tables primarily because I'm a
database programmer and this is an Access group. . . .


Er, I spend most of *my* time not with tables, but with user
interface objects. And that's where I need lots of flexibility, and
presentations that may change within different runtime contexts.
Indeed, it's certainly where code duplication is the most problem.
My earlier example of dialog forms that collect filtering criteria
for reports is a good example of exactly how the traditional
approach causes the proliferation of duplicate objects and duplicate
code. Generic class wrappers around dialog forms could give you an
object that you open and that returns a WHERE clause (assuming your
report recordsource is simple enough that it can be filtered by a
mere WHERE clause -- certain kinds can't).
. . . Yes, this design
pattern would apply equally well to many other kinds of things.
It applies well to database interactions because it keeps
everything that needs to know it even -is- a database interaction
in one place and it keeps all the details about each specific use
case in a single place in the code.


I don't see how my idLookup() function violates that same principle.
It's not real inheritance, but it's exactly what you've described,
in that it's upside-down, placing the "abstract" class as a
wrapper around the "concrete" class (in this case, a form).


Your application has a much different feel because each "Concrete"
class is much heavier, but I agree that what you are talking about
is probably the exact same design pattern I'm talking about.


Well, I've concluded that you're onto something, but that you're
stumbling over trying to apply pure OO terminology.

There is no inheritance.

Get used to it.

There *is* encapsulation, and that's how you get the benefits you
need.
Now, you can do the same kind of thing with a class module, as
you've shown, in that one of the properties of the "abstract"
class returns a reference to the "concrete" class.

So, it sort of makes sense to me from a structural level, but your
specific examples are sort of sabotaging my ability to understand
it clearly.


Perhaps, what I've said above will change your mind. It's good to
tuck the code that knows how a lookup is implemented (including
the fact that it's a database table at all) away from the code
that wants to look up a value.


Well, that's the basic principle of encapsulation, and, I'd think,
is self-evident.
And when described the way I've done above, it doesn't sound so
earth-shattering. But when you talk about using it with pure class
modules (not forms), it does seem to me to have value.

The key breakthrough for me here was thinking about the idea of
wanting to add a bunch of behaviors and properties to a lot of
objects of exactly the same type. When I looked at the concept of
a wrapper around a bunch of forms that added new capabilities to
all the forms thus encapsulated, it began to make sense.


Neat. I came up with the same pattern from a totally different
direction. I wanted to factor out just what was different from a
group of table interaction classes that had only a few differences
without having a bunch of classes wrapping a bunch of members of a
single root class.


As I said above, I think your insistence on the idea that you're
faking "inheritance" was the real problem for me. You're getting one
of the benefits that inheritance provides, but you're getting it by
an approach that turns the concept on its head.

And that's why I was so completely confused.
But I think it's a mistake to talk about it as though it's
inheritance. It really isn't at all -- it's just a form of
encapsulation that gives you the same benefits in regard to
eliminating code duplication as pure inheritance does.

(Of course, I'm also assuming that I've finally understood!)


I think you get it. If there's a better terminology for what I've
come up with, I'm interested in hearing it. Where I'm coming from
is that I started from a problem I knew very well how to handle if
I did have inheritance as a tool, and found an OK way to
approximate it without using inheritance. So, to me, the code
class correlates with the Abstract class, and the detail classes
correlate with Concrete classes because that's how they actually
would be implemented if we were doing true OOP.


To me the crucial concept is that you have a group of objects that
are the same at some level and you want to enhance all of them in
the same fashion. "Concrete" and "Abstract" just get in the way for
me there.

But I still think for the specific example you've given, you are
engaging in massive overkill. A simple function does *not* propagate
duplicate code.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 12 '05 #7

P: n/a
On Wed, 14 Apr 2004 00:05:39 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:ug********************************@4ax.com : ....
I chose a simple example on purpose, though not as simple as my
first case. Although what the class does is really just a DLookup,
the code that uses the class should be not have to know or care
that that's what the implementation is doing. That code should
not need to know field names or table names, or how to build
criteria strings. In fact, if external code is aware of these
things and does them more than once, it is duplicating code, and
what's being duplicated is not something the compiler can check
for you, so there is the possibility of typos, cut/paste errors,
and Search/Replace errors.


I don't see how my example would lead to duplication of any kind in
this regard.
I'm not sure what value you've gotten from this complexity. How
many times do you really have to do this kind of thing? Assuming a
structure for all your tables that you're working with of ID field
and value field (variant?), then I just don't see where the code
duplication would come in. Working up from the simplest case, I
end up with the code after my signature.


The duplication is not in the lookup code, the duplication is in
the application code that uses it. That's why what I'm talking
about is a design pattern to be applied in a given application,
not a library routine to be reused across applications. Using
your code in an application, then using that code multiple times
for tables with the same nature (lookup table), and use it
multiple times for the same table as well, the calling code has
duplication. . . .


What?

You're saying that:

Dim lngID as Long

lngID = idLookup("Benzene", "Gas", "tblGas", True)

is *code duplication*?


Now, I'm seeing what you're saying. In your case, the design pattern includes
the layout of the tables themselves, and that results in a simpler pattern
than mine so long as no behabiors have to be encapsulated. Actually, the
table name would be duplicated there, but that could be fixed by making it a
declared constant, so yes, that's less complex than my example and meets the
requirements demonstrated in my example.
My code can be used on any table of any kind that has these
characteristics:

1. has a unique ID PK

2. that ID field has a name derivable from the table name
("tblYourTable" -> "YourTableID")

3. has a second field with a non-duplicated text value.

Every table that has this characteristic could use this code.
OK, but what's to keep you, or more importantly the person who ends up having
to maintain your code later from changing the order of the fields. The
compiler can't tell you about such problems. Of course, I see that even that
could be worked around by placing a SELECT statement in a constant, and
passing that. Of course, the SELECT statement structure is still technically
duplicated, but if the constants are all together in one module, I guess any
deviation would be blatantly obvious, so the eye-ball code checker should be
sufficient.

Now, the only issue is adding code behavior. Since my code didn't add any
behavior, I will agree that your code would be better with the requirements as
shown, but if behavior were required, then I think mine would be the better
pattern.
The most trivial problem with it is that your tables may have
differnt naming conventions for the ID field, so you'd work around
that some way.
Actually, you could check the primary key property of the tabledef :).
But I don't see how my proposed solution increases code duplication
at all.
Now that I understand how you are using your code, I agree it doesn't given
that the table names are stored in constants.
But it isn't infinitely extensible, no, nor does it validate too
many things.

I'm just not getting it, Steve.
It sounds like you understand what my code does, so, given my acknowledgments
above, what's not to get. If you need overridable behaviors as well as static
value properties, I think the value of my approach becomes clear, and I do see
that my example fails to fully make the point since no behaviors are required.
Do you see, though, how it doesn sense in situations where behaviors are
required?

The case in which I employed this pattern, I was actually abstracting 2
complete separate database back-ends, one JET (for testing), and the other
PostgreSQL (Production). First of all, then, the connection string was not a
constant for the JET back-end, because it was determining the path of the
front-end (in this case, and Excel front-end), and using that path to find the
template database back-end contained in the same directory. Next, besides
values, there was a behavior. For the testing back-end, a new copy of the
back-end was made from the template on start-up using the PreInitialize member
of the Concrete class. For the production back-end, PreInitialize was left
empty.
. . . Of course, calling your code from my example would
very likely be desirable to remove duplication in another
direction presuming one did not also want to cache the recordset
which may or may not make sense in a given app, but it's better
that this also be something the calling code does not need to
know. If I want to cache or not cache my lookup table recordsets,
there should only be one place to make the change.


I'd think that lookup tables would be small enough that (and
probably stored locally) so that there's little reason to cache
them.


Actually, I sometimes like to cache them, so I always know I have the latest
copy as of some recent point in time. I tend to cache them only for the
lifespan of a single process.
Let's say I end up making this a client/server app on a busy
network, and I want to use disconnected recordsets for lookup
tables. With the code following the pattern I've outlined, only
the code in the "Abstract" class has to change. Neither the
calling code nor the Concrete classes has to change.


I only have to change my code in one place, to, within the function,
idLookup().

I just don't get it, Steve.


Right - it was I who didn't get your code. I get it now that you do have the
code in one place, and my objection does not apply.
Now, that could easily be generalized to returning any field based
on any criteria (via a parameter array), assuming you are
returning one field. It wouldn't produce horrid code, in my
opinion, but you would end up with a function overloaded with
arguments that would have to be passed in a very special format.
That always leads me in the direction of a class module, since
that can document much more easily what the requirements are for
getting accurate data out of it.

But I still don't see where there needs to be a base class and
then specific classes.


The specific classes encapsulate combinations of attributes (and
custom behaviors if/when they are added later) and keep each
combination in one and only one place in the code. That's the
sole reason for their existence


And how is it different than converting my simple function to a
single class?


That doesn't deal with adding behaviors and computed properties specific to
individual cases. I am agreeing that my example still failed to make the
case, but hopefully my explanation here has made explained its value in other
cases.
I don't see why you need either encapsulation or inheritance.
It only came up for me because I needed unique behaviors and computed
attributes for concrete cases.
I see only one level here.
I very, very often write simply one-line wrapper functions around
complex class modules (most often when I need to use the output in
a query, which can't refer directly to class modules), and your
example concrete classes have only a very small number of
properties/methods/members.

So, I'm just not seeing where the benefit is here:

1. your example seems more easily implemented in a much more
straightforward manner

2. I'm not able to think of an example that isn't likewise going
to be simple like that.

Each time you choose an example, you pick table interaction as
your subject. Table interactions are, I think, pretty easy to deal
with. Indeed, I kept thinking while reading your code that I
sometimes use tables to map relationships between tables and data
within those tables (tables as storage structures that themselves
drive the behavior of code; that is, tables that don't store data
about the app's end-user viewed data, but that store information
that is used entirely to drive code operation). The most common
example of this that I'm sure many people have used is when
importing/exporting data you often use a table to map fields on
one side to fields on the other.


A table makes sense when each case doesn't need to have a concrete
identity within the app, but needs to be flexible and data driven.


Yes, certainly. But I think you're depending on code for constructin
code structures that don't really *need* to be code structures. I
know your goal is having them be enforced by the compiler, but I'm
not sure that the value of that is so great as to justify the
enormous amount of extra code.


Well, it could even be done more simply with code structures as I acknowledged
above up until behaviors are involved. I get that a lookup system might never
need that, and if it did that would probably be because it evolved into
something more complex than what you would call a lookup system.
In my case, compile-time checking is a value. Also, in my
real-world case, I have encapsulated some behavior as well as
data, and you can't do that with a table. . . .


I understand that. All I'm saying is that tables can model
hierarchical relationships that aren't possible with the code
structures you have.


But the pattern I'm describing can be used to abstract away the interfaces to
2 similar heirarchies provided the abstract class does know the structure of
the heirarchy.
. . . My code uses Concrete
implementations of data sources, and the test source gets
automatically re-copied before it is opened. The example I have
shown here could and likely would evolve to have some encapsulated
behavior such as a table data integrity checker or some such
(probably not that in particular, but it's one real possibility).


I'm still foggy on why you need to do this the way you're doing it,
mostly because you've outlined the problem and not gone into detail.
I'm not sure the details would be any more enlightening, actually.


Alright then, I blew it again on providing a decent example. I'll try to post
a simple example of actually abstracting the entire database with a test
back-end just like my real-world case has to do.
Now, where I get a glimmer of potential for this kind of thing is
with objects that are not generic (like tables), but that are
specific to a particular application, but that are used in
multiple ways. We have a certain amount of "pass-through"
encapsulation available to us if we, say, wrap a class module
around a generic form. We can expose the properties, methods and
members of the encapsulated form directly through a form-type
property of the encapsulating class (including custom
properties/methods/members of the encapsulated form), while also
enhancing the encapasulated form with additional
propertis/methods/members.


Well, my examples deal with tables primarily because I'm a
database programmer and this is an Access group. . . .


Er, I spend most of *my* time not with tables, but with user
interface objects. And that's where I need lots of flexibility, and
presentations that may change within different runtime contexts.
Indeed, it's certainly where code duplication is the most problem.
My earlier example of dialog forms that collect filtering criteria
for reports is a good example of exactly how the traditional
approach causes the proliferation of duplicate objects and duplicate
code. Generic class wrappers around dialog forms could give you an
object that you open and that returns a WHERE clause (assuming your
report recordsource is simple enough that it can be filtered by a
mere WHERE clause -- certain kinds can't).


Agreed. User interfaces are where the bigger problems with duplication lie,
especially when trying to use Access bound forms for the interface.
. . . Yes, this design
pattern would apply equally well to many other kinds of things.
It applies well to database interactions because it keeps
everything that needs to know it even -is- a database interaction
in one place and it keeps all the details about each specific use
case in a single place in the code.


I don't see how my idLookup() function violates that same principle.


It doesn't - I just wasn't seeing it.
It's not real inheritance, but it's exactly what you've described,
in that it's upside-down, placing the "abstract" class as a
wrapper around the "concrete" class (in this case, a form).


Your application has a much different feel because each "Concrete"
class is much heavier, but I agree that what you are talking about
is probably the exact same design pattern I'm talking about.


Well, I've concluded that you're onto something, but that you're
stumbling over trying to apply pure OO terminology.

There is no inheritance.

Get used to it.

There *is* encapsulation, and that's how you get the benefits you
need.


Right. This was basically just all about how I finally figured out how to use
Enhapsulation and Polymorhpism to solve a problem I never previously saw a way
to solve without the benefit of inheritance.
Now, you can do the same kind of thing with a class module, as
you've shown, in that one of the properties of the "abstract"
class returns a reference to the "concrete" class.

So, it sort of makes sense to me from a structural level, but your
specific examples are sort of sabotaging my ability to understand
it clearly.


Perhaps, what I've said above will change your mind. It's good to
tuck the code that knows how a lookup is implemented (including
the fact that it's a database table at all) away from the code
that wants to look up a value.


Well, that's the basic principle of encapsulation, and, I'd think,
is self-evident.


The fact of wanting to do it is self-evident, the -how- was not at
self-evident to me in the case of trying to make a pluggable datbase back-end
for a data download process.
And when described the way I've done above, it doesn't sound so
earth-shattering. But when you talk about using it with pure class
modules (not forms), it does seem to me to have value.

The key breakthrough for me here was thinking about the idea of
wanting to add a bunch of behaviors and properties to a lot of
objects of exactly the same type. When I looked at the concept of
a wrapper around a bunch of forms that added new capabilities to
all the forms thus encapsulated, it began to make sense.


Neat. I came up with the same pattern from a totally different
direction. I wanted to factor out just what was different from a
group of table interaction classes that had only a few differences
without having a bunch of classes wrapping a bunch of members of a
single root class.


As I said above, I think your insistence on the idea that you're
faking "inheritance" was the real problem for me. You're getting one
of the benefits that inheritance provides, but you're getting it by
an approach that turns the concept on its head.

And that's why I was so completely confused.


I'm not sure I see it as turning the concept on its head. I still have a
class that contains the majority of the code structure, but is incomplete by
itself, and I have another class that provides the missing pieces to make the
first class able to function and handle a specific case. Is it so far off to
think of the first class as Abstract? I agree that to call the second class
Concrete is a bit rough when it in no way pretends to be exposed, but I can't
find a good term.
But I think it's a mistake to talk about it as though it's
inheritance. It really isn't at all -- it's just a form of
encapsulation that gives you the same benefits in regard to
eliminating code duplication as pure inheritance does.

(Of course, I'm also assuming that I've finally understood!)


I think you get it. If there's a better terminology for what I've
come up with, I'm interested in hearing it. Where I'm coming from
is that I started from a problem I knew very well how to handle if
I did have inheritance as a tool, and found an OK way to
approximate it without using inheritance. So, to me, the code
class correlates with the Abstract class, and the detail classes
correlate with Concrete classes because that's how they actually
would be implemented if we were doing true OOP.


To me the crucial concept is that you have a group of objects that
are the same at some level and you want to enhance all of them in
the same fashion. "Concrete" and "Abstract" just get in the way for
me there.

But I still think for the specific example you've given, you are
engaging in massive overkill. A simple function does *not* propagate
duplicate code.


I had misapprehended what your code actually did. I agree that functions
often provide complete abstractions and don't require duplicating code, though
there are several cases where they cannot, and patterns like the one I'm
trying to propose here must be employed.
Nov 12 '05 #8

P: n/a
rkc

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:ml********************************@4ax.com...
On Wed, 14 Apr 2004 00:05:39 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote: I'm not sure I see it as turning the concept on its head. I still have a
class that contains the majority of the code structure, but is incomplete by itself, and I have another class that provides the missing pieces to make the first class able to function and handle a specific case. Is it so far off to think of the first class as Abstract? I agree that to call the second class Concrete is a bit rough when it in no way pretends to be exposed, but I can't find a good term.


Your first class is not abstract. It can't even be confused with being
abstract.

In VB an interface is abstract. It's abstract because it only has signatures
of
properties and methods. They have no functionality. The functionality is
left to
a concrete class (any class that can be instantiated) that implements the
interface
class. What you are doing is clear to me. The value of what you are doing is
clear to me. Unfortunately it is not because of what you have presented
so far. Raise a glass to the fourth try being a winner.
Nov 12 '05 #9

P: n/a
On Wed, 14 Apr 2004 02:53:54 GMT, "rkc" <rk*@yabba.dabba.do.rochester.rr.bomb>
wrote:

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:ml********************************@4ax.com.. .
On Wed, 14 Apr 2004 00:05:39 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote:

I'm not sure I see it as turning the concept on its head. I still have a
class that contains the majority of the code structure, but is incomplete

by
itself, and I have another class that provides the missing pieces to make

the
first class able to function and handle a specific case. Is it so far off

to
think of the first class as Abstract? I agree that to call the second

class
Concrete is a bit rough when it in no way pretends to be exposed, but I

can't
find a good term.


Your first class is not abstract. It can't even be confused with being
abstract.

In VB an interface is abstract. It's abstract because it only has signatures of
properties and methods. They have no functionality. The functionality is left to
a concrete class (any class that can be instantiated) that implements the
interface class. What you are doing is clear to me. The value of what you are doing is
clear to me. Unfortunately it is not because of what you have presented
so far. Raise a glass to the fourth try being a winner.


An abstract class is not one that contains only interfaces, it is one that is
incomplete without a supporting class. If it's that unclear to everyone but
me, though, then it is inherently unclear, and my terminology needs work. All
suggestions welcome.

As for my next try, I'll just strip down the real, production code. It may be
a few days because the code is in flux, so I'll have to get it to the next
stable point, then rip out enough to protect my client.
Nov 12 '05 #10

P: n/a
Steve Jorgensen <no****@nospam.nospam> wrote in
news:ml********************************@4ax.com:
On Wed, 14 Apr 2004 00:05:39 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:ug********************************@4ax.co m:
The duplication is not in the lookup code, the duplication is in
the application code that uses it. That's why what I'm talking
about is a design pattern to be applied in a given application,
not a library routine to be reused across applications. Using
your code in an application, then using that code multiple times
for tables with the same nature (lookup table), and use it
multiple times for the same table as well, the calling code has
duplication. . . .
What?

You're saying that:

Dim lngID as Long

lngID = idLookup("Benzene", "Gas", "tblGas", True)

is *code duplication*?


Now, I'm seeing what you're saying. In your case, the design
pattern includes the layout of the tables themselves, and that
results in a simpler pattern than mine so long as no behabiors
have to be encapsulated. Actually, the table name would be
duplicated there, but that could be fixed by making it a declared
constant, so yes, that's less complex than my example and meets
the requirements demonstrated in my example.
My code can be used on any table of any kind that has these
characteristics:

1. has a unique ID PK

2. that ID field has a name derivable from the table name
("tblYourTable" -> "YourTableID")

3. has a second field with a non-duplicated text value.

Every table that has this characteristic could use this code.


OK, but what's to keep you, or more importantly the person who
ends up having to maintain your code later from changing the order
of the fields. . . .


My code has no dependency on the order of the fields. It does have a
dependency on a naming convention for the PK and table. As I said,
though, that assumption could be removed by passing the ID field
name to the function.
. . . The compiler can't tell you about such problems.
Of course, I see that even that could be worked around by placing
a SELECT statement in a constant, and passing that. Of course,
the SELECT statement structure is still technically duplicated,
but if the constants are all together in one module, I guess any
deviation would be blatantly obvious, so the eye-ball code checker
should be sufficient.
I don't understand your point here. You seem to be under the
misapprehension that the order of the fields in my SELECT statement
have some effect on how the code works. There is no such issue.

The only dependency is that the code requires certain conventions
for the layout of the tables involved. Given that the entire premise
for the task is that that there are a group of tables with certain
shared characteristics, I don't see the issue -- the assumption that
the PK name can be derived from the table name can be very easily
worked out of the code by adding an input parameter to the function.

The compiler can't evaluate that, but, well, the compiler can't
evaluate whether or not the table is of a valid structure for the
application of the lookup/add to make any sense. That is, using it
against a table that doesn't have a unique relationship between the
ID field and the text field being looked up/added would produce bad
results, too, and, unless you write your code to give a warning when
more than one record is returned, you've got a potential for
mis-use.

Yes, you could write your code to prevent such a mis-use (and the
resulting random results), but you can't enforce the requirement in
the *structure* of the code (so that the compiler enforces it) --
it's only something that can be tested at runtime.
Now, the only issue is adding code behavior. Since my code didn't
add any behavior, I will agree that your code would be better with
the requirements as shown, but if behavior were required, then I
think mine would be the better pattern.
I'm not seeing what kind of additional behavior would justify your
Byzantine solution, but again, this may be because of the specifity
of the example, and not an issue of the general case.
The most trivial problem with it is that your tables may have
differnt naming conventions for the ID field, so you'd work around
that some way.


Actually, you could check the primary key property of the tabledef
:).


What if it wasn't an ID field? Your code assumes that it's a long,
so your code has that assumption built in, as well, so I didn't
consider it worth worrying about the PK.

And you might be using a natural key PK and also have a surrogate
key, and the PK could be multi-column, so you're quickly getting
into areas that are outside the scope of the example you were
presenting, so I didn't consider it necessary for my solution to
address those problems.
But I don't see how my proposed solution increases code
duplication at all.


Now that I understand how you are using your code, I agree it
doesn't given that the table names are stored in constants.


Eh? What are you talking about? The table names are not stored in
constants. They are passed to the function when it's called.
But it isn't infinitely extensible, no, nor does it validate too
many things.

I'm just not getting it, Steve.


It sounds like you understand what my code does, so, given my
acknowledgments above, what's not to get. If you need overridable
behaviors as well as static value properties, I think the value of
my approach becomes clear, and I do see that my example fails to
fully make the point since no behaviors are required. Do you see,
though, how it doesn sense in situations where behaviors are
required?


I don't really know what you mean by "behaviors."
The case in which I employed this pattern, I was actually
abstracting 2 complete separate database back-ends, one JET (for
testing), and the other PostgreSQL (Production). First of all,
then, the connection string was not a constant for the JET
back-end, because it was determining the path of the front-end (in
this case, and Excel front-end), and using that path to find the
template database back-end contained in the same directory. Next,
besides values, there was a behavior. For the testing back-end, a
new copy of the back-end was made from the template on start-up
using the PreInitialize member of the Concrete class. For the
production back-end, PreInitialize was left empty.


I don't see that faking inheritance plays any role here at all.
. . . Of course, calling your code from my example would
very likely be desirable to remove duplication in another
direction presuming one did not also want to cache the recordset
which may or may not make sense in a given app, but it's better
that this also be something the calling code does not need to
know. If I want to cache or not cache my lookup table
recordsets, there should only be one place to make the change.


I'd think that lookup tables would be small enough that (and
probably stored locally) so that there's little reason to cache
them.


Actually, I sometimes like to cache them, so I always know I have
the latest copy as of some recent point in time. I tend to cache
them only for the lifespan of a single process.


The problem with caching is what you do about updates. If you cache
for too long, you may need to re-retrieve. I just can't see any
reason to cache something as small as a lookup table, assuming it's
not stored server-side, of course.
Let's say I end up making this a client/server app on a busy
network, and I want to use disconnected recordsets for lookup
tables. With the code following the pattern I've outlined, only
the code in the "Abstract" class has to change. Neither the
calling code nor the Concrete classes has to change.


I only have to change my code in one place, to, within the
function, idLookup().

I just don't get it, Steve.


Right - it was I who didn't get your code. I get it now that you
do have the code in one place, and my objection does not apply.


Well, this is the whole source of my problem -- you have come up
with an extremely complex method of reducing code duplication,
whereas I've come up with one that is very straightforward and
simple. What's the advantage in your approach?

As I said, if I wanted to do a multi-field lookup/add, I'd most
likely not use a function (with a parameter array), but wrap it in a
class module, and probably use an array internal to the class to
store the fieldname/value pairs (from which you'd construct the
WHERE clause, and if no match is found, looping through that array
to insert the data). The reason for that is that I could have my
method for assigning fieldname/value pairs validate at the compiler
level, whereas a parameter array could only be parsed for valid
pairs at runtime.

Of course, one could still pass invalid field names and invalid data
types.
Now, that could easily be generalized to returning any field
based on any criteria (via a parameter array), assuming you are
returning one field. It wouldn't produce horrid code, in my
opinion, but you would end up with a function overloaded with
arguments that would have to be passed in a very special format.
That always leads me in the direction of a class module, since
that can document much more easily what the requirements are for
getting accurate data out of it.

But I still don't see where there needs to be a base class and
then specific classes.

The specific classes encapsulate combinations of attributes (and
custom behaviors if/when they are added later) and keep each
combination in one and only one place in the code. That's the
sole reason for their existence


And how is it different than converting my simple function to a
single class?


That doesn't deal with adding behaviors and computed properties
specific to individual cases. I am agreeing that my example still
failed to make the case, but hopefully my explanation here has
made explained its value in other cases.


I'm stuck not being able to conceive in the abstract where the
advantage of your approach is coming from.
I don't see why you need either encapsulation or inheritance.


It only came up for me because I needed unique behaviors and
computed attributes for concrete cases.


But I don't see the issue there, either. Indeed, I'd say you've got
it backwards -- your abstract class should have all the behaviors,
and your concrete one should use the particular ones specific to
that specific object.

I guess I'm seeing now why inheritance would be so helpful, since
what I described was only a matter of enhancing a group of objects
that had a set of identical attributes, whereas you're saying there
are group of objects that have a set of attributes in common, but
that also have individual properties that are important to be usable
at a higher level, without having to duplicate code at multiple
levels.

OK -- it's gotten through my thick skull now.

In my criteria dialog example, I was assuming that you'd use common
collections (like the form's Controls collection, or, more likely, a
custom collection) to get what you needed.

Another thing that I've done is to not build a really complex class,
but to break down little bits of functionality into individual
classes, then if more than one of the functions is needed in a
particular context, you use as many of the different classes as
needed. This keeps the individual classes much simpler, though it
does mean you have to keep track of a bit more in using them.

[]
In my case, compile-time checking is a value. Also, in my
real-world case, I have encapsulated some behavior as well as
data, and you can't do that with a table. . . .


I understand that. All I'm saying is that tables can model
hierarchical relationships that aren't possible with the code
structures you have.


But the pattern I'm describing can be used to abstract away the
interfaces to 2 similar heirarchies provided the abstract class
does know the structure of the heirarchy.


And my point is that I often find that table-driven code can account
for the exceptions far better than code that deals with the
exceptions.
. . . My code uses Concrete
implementations of data sources, and the test source gets
automatically re-copied before it is opened. The example I have
shown here could and likely would evolve to have some
encapsulated behavior such as a table data integrity checker or
some such (probably not that in particular, but it's one real
possibility).


I'm still foggy on why you need to do this the way you're doing
it, mostly because you've outlined the problem and not gone into
detail. I'm not sure the details would be any more enlightening,
actually.


Alright then, I blew it again on providing a decent example. I'll
try to post a simple example of actually abstracting the entire
database with a test back-end just like my real-world case has to
do.


I suspect that doing so for us will help you nail down what you're
doing.

Just yesterday I was coming home from a client and thinking through
a posting I was going to make asking for opinions on how to solve a
schema problem that I've been struggling with over the last two
weeks. In the process of formulating in my head how I'd present the
problem, the solution just popped right into my head -- I was
thinking of denormalizing, and I suddenly realized that the problem
was that I had not fully normalized my original data structure. By
adding a child table and moving certain data into it, I got rid of a
problem I was having with groups of records that were related, but
that included no data in them to reliably identify the relations.
Formulating my problem forced me to see how obvious the solution
was.

Maybe the same process will help you.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 12 '05 #11

P: n/a
rkc

"Steve Jorgensen" <no****@nospam.nospam> wrote in message
news:qo********************************@4ax.com...
On Wed, 14 Apr 2004 02:53:54 GMT, "rkc" <rk*@yabba.dabba.do.rochester.rr.bomb> wrote: An abstract class is not one that contains only interfaces, it is one that is incomplete without a supporting class.
O.K. I'll buy that definition for this discussion.
So in your mind a concrete class isn't just one that can be instantiated
it's
one that doesn't need to contain (what you and dwf are calling encapsulate,
I guess) any other object(s) to be of some use.
If it's that unclear to everyone but
me, though, then it is inherently unclear, and my terminology needs work. All suggestions welcome.


I don't think anyone even cares except me and dw and we are confused in
totally different ways.

Nov 12 '05 #12

P: n/a
"rkc" <rk*@yabba.dabba.do.rochester.rr.bomb> wrote in
news:5b******************@twister.nyroc.rr.com:
I don't think anyone even cares except me and dw and we are
confused in totally different ways.


We could be confused in exactly the same way, but confusion might be
like Nulls, and not comparable.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc
Nov 12 '05 #13

P: n/a
On Wed, 14 Apr 2004 19:29:36 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:ml********************************@4ax.com :
On Wed, 14 Apr 2004 00:05:39 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:ug********************************@4ax.com : ....
OK, but what's to keep you, or more importantly the person who
ends up having to maintain your code later from changing the order
of the fields. . . .


My code has no dependency on the order of the fields. It does have a
dependency on a naming convention for the PK and table. As I said,
though, that assumption could be removed by passing the ID field
name to the function.


OK, I guess I'm batting 1000. I thought you were saying that the order of the
fields mattered, not the name. I have less of an issue with that. I do think
that if someone changes a field name, they should only have one place to
change it in the code, but we all know that's too onerous a condition in an
Access app anyway.

The only problem would be making it obvious to someone maintaining the code
that the code is dependent upon the naming convention. Table and field naming
constraints are always an issue in Access anyway, though, and there's not much
short of an act of congress to do about it, so I guess I can't object.
. . . The compiler can't tell you about such problems.
Of course, I see that even that could be worked around by placing
a SELECT statement in a constant, and passing that. Of course,
the SELECT statement structure is still technically duplicated,
but if the constants are all together in one module, I guess any
deviation would be blatantly obvious, so the eye-ball code checker
should be sufficient.


I don't understand your point here. You seem to be under the
misapprehension that the order of the fields in my SELECT statement
have some effect on how the code works. There is no such issue.


Yes, yes. See above.
The only dependency is that the code requires certain conventions
for the layout of the tables involved. Given that the entire premise
for the task is that that there are a group of tables with certain
shared characteristics, I don't see the issue -- the assumption that
the PK name can be derived from the table name can be very easily
worked out of the code by adding an input parameter to the function.
I don't agree with the idea of using an input parameter because that restores
my earlier objection over duplication. Now, you would be passing a table and
a field to each invocation, and they must match up correctly in multiple
places. If you were to make a wrapper function for each table that passes the
table name and ID field name, that would take care of it. I still actually
prefer sticking with the naming convention, and making sure something chokes
up-front if a naming convention is broken.
The compiler can't evaluate that, but, well, the compiler can't
evaluate whether or not the table is of a valid structure for the
application of the lookup/add to make any sense. That is, using it
against a table that doesn't have a unique relationship between the
ID field and the text field being looked up/added would produce bad
results, too, and, unless you write your code to give a warning when
more than one record is returned, you've got a potential for
mis-use.
Right. I think a start-up audit would be a good substitute for a compile-time
check in this case, and it can be relied upon because Access provides a
collection of all tables.
Yes, you could write your code to prevent such a mis-use (and the
resulting random results), but you can't enforce the requirement in
the *structure* of the code (so that the compiler enforces it) --
it's only something that can be tested at runtime.
As above, I'm actually OK with that, but would prefer to put the run-time
checks somewhere that they'll be sure to flag the programmer before the code
goes to production. That's assuming there is a discernable way to make sure
the necessary checks can all be identifed and run.
Now, the only issue is adding code behavior. Since my code didn't
add any behavior, I will agree that your code would be better with
the requirements as shown, but if behavior were required, then I
think mine would be the better pattern.


I'm not seeing what kind of additional behavior would justify your
Byzantine solution, but again, this may be because of the specifity
of the example, and not an issue of the general case.


You've already convinced me that my example doesn't justify the pattern I'm
describing. That's why I described the real-world situation that drove me to
this pattern in the first place. Eventually, I'll try to post an example
based on that case. I guess this is a lesson for the future to use examples
that don't deviate much from the original cases so as to ensure that the
mapping will make sense.
The most trivial problem with it is that your tables may have
differnt naming conventions for the ID field, so you'd work around
that some way.


Actually, you could check the primary key property of the tabledef
:).


What if it wasn't an ID field? Your code assumes that it's a long,
so your code has that assumption built in, as well, so I didn't
consider it worth worrying about the PK.


Well, I was just saying that could be a way around having a different naming
convention for the key. Probably a better answer is just to wrap any table
the programmer doesn't have control over the structure of in a query that maps
the right naming convention.
And you might be using a natural key PK and also have a surrogate
key, and the PK could be multi-column, so you're quickly getting
into areas that are outside the scope of the example you were
presenting, so I didn't consider it necessary for my solution to
address those problems.
The problems I do think it's necessary to address as much as possible are the
ones that allow side effects to creep in. Duplication is the biggest buggaboo
for that. When code relates to tables, there's no compile-time check
possible, so a good run-time check is a good second best.
But I don't see how my proposed solution increases code
duplication at all.


Now that I understand how you are using your code, I agree it
doesn't given that the table names are stored in constants.


Eh? What are you talking about? The table names are not stored in
constants. They are passed to the function when it's called.
But it isn't infinitely extensible, no, nor does it validate too
many things.

I'm just not getting it, Steve.


It sounds like you understand what my code does, so, given my
acknowledgments above, what's not to get. If you need overridable
behaviors as well as static value properties, I think the value of
my approach becomes clear, and I do see that my example fails to
fully make the point since no behaviors are required. Do you see,
though, how it doesn sense in situations where behaviors are
required?


I don't really know what you mean by "behaviors."


Like the pre-copy and directory mapping described below.
The case in which I employed this pattern, I was actually
abstracting 2 complete separate database back-ends, one JET (for
testing), and the other PostgreSQL (Production). First of all,
then, the connection string was not a constant for the JET
back-end, because it was determining the path of the front-end (in
this case, and Excel front-end), and using that path to find the
template database back-end contained in the same directory. Next,
besides values, there was a behavior. For the testing back-end, a
new copy of the back-end was made from the template on start-up
using the PreInitialize member of the Concrete class. For the
production back-end, PreInitialize was left empty.


I don't see that faking inheritance plays any role here at all.


It's what I've been talking about all along. Using virtual private members
and inheritance is the only way I've previously seen to have an abstract core
that can be made concrete with the addition of a group of simple values and
behaviors, keep the set of related values and behaviors together in one place,
remove duplication, and have encapsulation. This whole pattern was based on
trying to do all those things without inheritance.
. . . Of course, calling your code from my example would
very likely be desirable to remove duplication in another
direction presuming one did not also want to cache the recordset
which may or may not make sense in a given app, but it's better
that this also be something the calling code does not need to
know. If I want to cache or not cache my lookup table
recordsets, there should only be one place to make the change.

I'd think that lookup tables would be small enough that (and
probably stored locally) so that there's little reason to cache
them.


Actually, I sometimes like to cache them, so I always know I have
the latest copy as of some recent point in time. I tend to cache
them only for the lifespan of a single process.


The problem with caching is what you do about updates. If you cache
for too long, you may need to re-retrieve. I just can't see any
reason to cache something as small as a lookup table, assuming it's
not stored server-side, of course.


Well, that's precisely the reason. If you want to have the server be the
authority for what the contents of the lookup table should be, but you don't
want to hit the server repeatedly, you cache it. Since lookup table values
change rarely, if an update does happen, you just try to add it, re-query the
table after adding, and check for errors in the process. If a duplication
error occurs, it can simply be ignored, and the re-query will pick up the
matching entry.
Let's say I end up making this a client/server app on a busy
network, and I want to use disconnected recordsets for lookup
tables. With the code following the pattern I've outlined, only
the code in the "Abstract" class has to change. Neither the
calling code nor the Concrete classes has to change.

I only have to change my code in one place, to, within the
function, idLookup().

I just don't get it, Steve.


Right - it was I who didn't get your code. I get it now that you
do have the code in one place, and my objection does not apply.


Well, this is the whole source of my problem -- you have come up
with an extremely complex method of reducing code duplication,
whereas I've come up with one that is very straightforward and
simple. What's the advantage in your approach?


None at all in the case of a lookup table. Yours wins hands down. It
wouldn't solve the problems in the code that drove me to this pattern, though.

....
....
That doesn't deal with adding behaviors and computed properties
specific to individual cases. I am agreeing that my example still
failed to make the case, but hopefully my explanation here has
made explained its value in other cases.


I'm stuck not being able to conceive in the abstract where the
advantage of your approach is coming from.
I don't see why you need either encapsulation or inheritance.


It only came up for me because I needed unique behaviors and
computed attributes for concrete cases.


But I don't see the issue there, either. Indeed, I'd say you've got
it backwards -- your abstract class should have all the behaviors,
and your concrete one should use the particular ones specific to
that specific object.


That's not what I have. I have the common behaviors in the Abstract class to
avoid duplication, and the specific behaviors in the "Concrete" classes.
I guess I'm seeing now why inheritance would be so helpful, since
what I described was only a matter of enhancing a group of objects
that had a set of identical attributes, whereas you're saying there
are group of objects that have a set of attributes in common, but
that also have individual properties that are important to be usable
at a higher level, without having to duplicate code at multiple
levels.

OK -- it's gotten through my thick skull now.
Give the man a cigar <g>. Of course, I might have gotten through your skull
faster with a nail gun than with a squirt gun. <G>
In my criteria dialog example, I was assuming that you'd use common
collections (like the form's Controls collection, or, more likely, a
custom collection) to get what you needed.

Another thing that I've done is to not build a really complex class,
but to break down little bits of functionality into individual
classes, then if more than one of the functions is needed in a
particular context, you use as many of the different classes as
needed. This keeps the individual classes much simpler, though it
does mean you have to keep track of a bit more in using them.


I suppose that makes sense if the cases for using the different classes have
low correlation. Then again, if they had high correlation, you wouldn't have
thought of splitting them up.

....
Alright then, I blew it again on providing a decent example. I'll
try to post a simple example of actually abstracting the entire
database with a test back-end just like my real-world case has to
do.


I suspect that doing so for us will help you nail down what you're
doing.

Just yesterday I was coming home from a client and thinking through
a posting I was going to make asking for opinions on how to solve a
schema problem that I've been struggling with over the last two
weeks. In the process of formulating in my head how I'd present the
problem, the solution just popped right into my head -- I was
thinking of denormalizing, and I suddenly realized that the problem
was that I had not fully normalized my original data structure. By
adding a child table and moving certain data into it, I got rid of a
problem I was having with groups of records that were related, but
that included no data in them to reliably identify the relations.
Formulating my problem forced me to see how obvious the solution
was.

Maybe the same process will help you.


It often does. In fact, I had a similar incident at work today. I was asked
to add a system to track circuti boards under service contract. Initially,
the table could be mostly populated by checking what boards had been
previously sent out to the client on RAs, but never returned. I could see
that I would have to add a system to keep the table of boards under agreement
in sync with the RA details, and reporting requirements would be such that the
boards in service table list need to have a transaction history.

But wait, we don't need the static table of boards under agreement because
that can be queried from the transaction records. Ah, and since there's very
little difference between the boards in service and the ones sent out on RAs,
but not returned, the RA table practically -is- the boards under service
agreement table! All I have to do is at RA detail type codes to represent
boards added to or removed from the service agreement that were not shipped to
or from my client's company. Potentially several days to a week's worth of
work to implement, import, and debug just turned into a 3 hour, minor
maintenance job.
Nov 12 '05 #14

P: n/a
Steve Jorgensen <no****@nospam.nospam> wrote in
news:3i********************************@4ax.com:
On Wed, 14 Apr 2004 19:29:36 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote:
Steve Jorgensen <no****@nospam.nospam> wrote in
news:ml********************************@4ax.co m:
On Wed, 14 Apr 2004 00:05:39 GMT, "David W. Fenton"
<dX********@bway.net.invalid> wrote:

Steve Jorgensen <no****@nospam.nospam> wrote in
news:ug********************************@4ax.co m:

...
OK, but what's to keep you, or more importantly the person who
ends up having to maintain your code later from changing the
order of the fields. . . .


My code has no dependency on the order of the fields. It does have
a dependency on a naming convention for the PK and table. As I
said, though, that assumption could be removed by passing the ID
field name to the function.


OK, I guess I'm batting 1000. I thought you were saying that the
order of the fields mattered, not the name. I have less of an
issue with that. I do think that if someone changes a field name,
they should only have one place to change it in the code, but we
all know that's too onerous a condition in an Access app anyway.


The name is not in the code, but the alrgorithm for figuring out the
ID field name from the table name is hard-coded. If you created
tables that didn't follow that format, then you'd have to change the
code (or pass it a query with appropriately aliased fieldnames
instead of a table).
The only problem would be making it obvious to someone maintaining
the code that the code is dependent upon the naming convention.
Table and field naming constraints are always an issue in Access
anyway, though, and there's not much short of an act of congress
to do about it, so I guess I can't object.


Well, the code ought to have a comment in there declaring the
dependency.

But it wouldn't be that hard to get rid of the dependency by simply
adding a parameter for the ID field name. Indeed, I'd make it
optional, since I hardly ever have a table that violates the rule,
but occasionally it happens.

[]
The only dependency is that the code requires certain conventions
for the layout of the tables involved. Given that the entire
premise for the task is that that there are a group of tables with
certain shared characteristics, I don't see the issue -- the
assumption that the PK name can be derived from the table name can
be very easily worked out of the code by adding an input parameter
to the function.


I don't agree with the idea of using an input parameter because
that restores my earlier objection over duplication. Now, you
would be passing a table and a field to each invocation, and they
must match up correctly in multiple places. If you were to make a
wrapper function for each table that passes the table name and ID
field name, that would take care of it. I still actually prefer
sticking with the naming convention, and making sure something
chokes up-front if a naming convention is broken.


Well, rather than be dogmatic about it, I think I'd go with the
optional ID field name parameter.
The compiler can't evaluate that, but, well, the compiler can't
evaluate whether or not the table is of a valid structure for the
application of the lookup/add to make any sense. That is, using it
against a table that doesn't have a unique relationship between
the ID field and the text field being looked up/added would
produce bad results, too, and, unless you write your code to give
a warning when more than one record is returned, you've got a
potential for mis-use.


Right. I think a start-up audit would be a good substitute for a
compile-time check in this case, and it can be relied upon because
Access provides a collection of all tables.


Ack. Sounds dreadful, as it's a mistake that *can't* happen at
runtime. Well, what I mean is that any testing of the code at all
will reveal the error. Code delivered without discovering it is code
that has not been tested.

I believe in startup testing only in applications where the data can
alter the behavior.

Indeed, I don't know exactly how you'd go about doing an audit for
something like this. Maybe I'm not understanding what you're
suggesting.
Yes, you could write your code to prevent such a mis-use (and the
resulting random results), but you can't enforce the requirement
in the *structure* of the code (so that the compiler enforces it)
-- it's only something that can be tested at runtime.


As above, I'm actually OK with that, but would prefer to put the
run-time checks somewhere that they'll be sure to flag the
programmer before the code goes to production. That's assuming
there is a discernable way to make sure the necessary checks can
all be identifed and run.


Wouldn't even the most haphazard testing flush out the error?

The thing to do, then, would be to make the failure graceful, and
have it just not do anything other than report a problem to the
user.

It does seem to me that simply testing for .Recordcount>1 (yes, it
would require a .MoveLast) would suffice, and then pop up an error
message telling the user that the addition of the new value failed
with appropriate information for the bug report. If you've got a bug
logging system in place, this would be an ideal place to use it.

[]
The most trivial problem with it is that your tables may have
differnt naming conventions for the ID field, so you'd work
around that some way.

Actually, you could check the primary key property of the
tabledef
:).


What if it wasn't an ID field? Your code assumes that it's a long,
so your code has that assumption built in, as well, so I didn't
consider it worth worrying about the PK.


Well, I was just saying that could be a way around having a
different naming convention for the key. Probably a better answer
is just to wrap any table the programmer doesn't have control over
the structure of in a query that maps the right naming convention.


Great minds think alike!
And you might be using a natural key PK and also have a surrogate
key, and the PK could be multi-column, so you're quickly getting
into areas that are outside the scope of the example you were
presenting, so I didn't consider it necessary for my solution to
address those problems.


The problems I do think it's necessary to address as much as
possible are the ones that allow side effects to creep in.
Duplication is the biggest buggaboo for that. When code relates
to tables, there's no compile-time check possible, so a good
run-time check is a good second best.


But your whole structure has a whole buttload of assumptions built
into it, assumptions that it doesn't test for as being correct. My
solution had the same solutions.

I don't see this as a problem.

And it seems to me, Steve, that you've now swung very wildly in the
opposite direction of your recent XP crusade. You're doing precisely
the thing your comments about the virtues of XP were criticising.

[]
The case in which I employed this pattern, I was actually
abstracting 2 complete separate database back-ends, one JET (for
testing), and the other PostgreSQL (Production). First of all,
then, the connection string was not a constant for the JET
back-end, because it was determining the path of the front-end
(in this case, and Excel front-end), and using that path to find
the template database back-end contained in the same directory.
Next, besides values, there was a behavior. For the testing
back-end, a new copy of the back-end was made from the template
on start-up using the PreInitialize member of the Concrete
class. For the production back-end, PreInitialize was left
empty.


I don't see that faking inheritance plays any role here at all.


It's what I've been talking about all along. Using virtual
private members and inheritance is the only way I've previously
seen to have an abstract core that can be made concrete with the
addition of a group of simple values and behaviors, keep the set
of related values and behaviors together in one place, remove
duplication, and have encapsulation. This whole pattern was based
on trying to do all those things without inheritance.


This is exactly where I could see bringing a data table into the
picture could help. That would list your members and could be
exposed at any level, with as much navigation and maneuverability
and filterability and sortability as you have with any recordset.

Instead of storing a boatload of constants and private members in
code modules, put them in a table.

Yes, you'd need some kind of validation routine to make sure they
stayed valid, but that would only need to be run when you changed
the values (just as constants only get meaningfully tested when they
change).

[]
The problem with caching is what you do about updates. If you
cache for too long, you may need to re-retrieve. I just can't see
any reason to cache something as small as a lookup table, assuming
it's not stored server-side, of course.


Well, that's precisely the reason. If you want to have the server
be the authority for what the contents of the lookup table should
be, but you don't want to hit the server repeatedly, you cache it.
Since lookup table values change rarely, if an update does
happen, you just try to add it, re-query the table after adding,
and check for errors in the process. If a duplication error
occurs, it can simply be ignored, and the re-query will pick up
the matching entry.


I was assuming a structure where the lookups were already stored
client-side, either authoritatively, or as a local cache from the
server version.

[]
Well, this is the whole source of my problem -- you have come up
with an extremely complex method of reducing code duplication,
whereas I've come up with one that is very straightforward and
simple. What's the advantage in your approach?


None at all in the case of a lookup table. Yours wins hands down.
It wouldn't solve the problems in the code that drove me to this
pattern, though.


A function, you mean. But the solution was as simple as the problem
to be solved. Given a different problem, I'd propose a different
solution.

I can't say that I'd have a simpler solution, but so far you haven't
explained anything that makes me see an advantage to your approach,
except when you're trying to handle a group of objects of
non-identical structure through a common interface. I'm not certain
that your idea is going to be widely adaptable. It all depends on
the kinds of objects you're trying to group together and what
interfaces they natively provide for getting to their component
attributes.

I've been mumbling repeatedly about the idea of handling multiple
dialog forms for filtering reports through a common interface, and
the reason that would work with only a single wrapper class is
because forms are rich objects that have a number of useful default
collections that probably allow you to do what you need with them.

[]
I don't see why you need either encapsulation or inheritance.

It only came up for me because I needed unique behaviors and
computed attributes for concrete cases.


But I don't see the issue there, either. Indeed, I'd say you've
got it backwards -- your abstract class should have all the
behaviors, and your concrete one should use the particular ones
specific to that specific object.


That's not what I have. I have the common behaviors in the
Abstract class to avoid duplication, and the specific behaviors in
the "Concrete" classes.


At this point, I'm just lost.

[]
In my criteria dialog example, I was assuming that you'd use
common collections (like the form's Controls collection, or, more
likely, a custom collection) to get what you needed.

Another thing that I've done is to not build a really complex
class, but to break down little bits of functionality into
individual classes, then if more than one of the functions is
needed in a particular context, you use as many of the different
classes as needed. This keeps the individual classes much simpler,
though it does mean you have to keep track of a bit more in using
them.


I suppose that makes sense if the cases for using the different
classes have low correlation. Then again, if they had high
correlation, you wouldn't have thought of splitting them up.


Well, it does offer a mix-and-match approach using a lot of
non-complex objects, instead of creating one or two monolithic
objects that do it all.

Hint, hint.
Alright then, I blew it again on providing a decent example.
I'll try to post a simple example of actually abstracting the
entire database with a test back-end just like my real-world
case has to do.


I suspect that doing so for us will help you nail down what you're
doing.

Just yesterday I was coming home from a client and thinking
through a posting I was going to make asking for opinions on how
to solve a schema problem that I've been struggling with over the
last two weeks. In the process of formulating in my head how I'd
present the problem, the solution just popped right into my head
-- I was thinking of denormalizing, and I suddenly realized that
the problem was that I had not fully normalized my original data
structure. By adding a child table and moving certain data into
it, I got rid of a problem I was having with groups of records
that were related, but that included no data in them to reliably
identify the relations. Formulating my problem forced me to see
how obvious the solution was.

Maybe the same process will help you.


It often does. In fact, I had a similar incident at work today.
I was asked to add a system to track circuti boards under service
contract. Initially, the table could be mostly populated by
checking what boards had been previously sent out to the client on
RAs, but never returned. I could see that I would have to add a
system to keep the table of boards under agreement in sync with
the RA details, and reporting requirements would be such that the
boards in service table list need to have a transaction history.

But wait, we don't need the static table of boards under agreement
because that can be queried from the transaction records. Ah, and
since there's very little difference between the boards in service
and the ones sent out on RAs, but not returned, the RA table
practically -is- the boards under service agreement table! All I
have to do is at RA detail type codes to represent boards added to
or removed from the service agreement that were not shipped to or
from my client's company. Potentially several days to a week's
worth of work to implement, import, and debug just turned into a 3
hour, minor maintenance job.


I did it again today with a sorting problem I was having. I couldn't
figure out why things weren't coming out in the right order, and in
the process of writing it up, figured it out.

I realized I've assumed a lot of things about Nulls that are
incorrect, such as treating these two ORDER BY clauses as equivalent
when Field2 is populated with nothing but Nulls:

ORDER BY Field1, Field2, Field3, Field4

and

ORDER BY Field1, Field2 & Field3, Field4

Put that way, it's blazingly obvious that they aren't the same, but
it took a while to sink in.

Anyway, I think I'd like to hear what you're *really* doing with
this fake inheritance thing. I can't guarantee I'd understand a word
of it, though.

--
David W. Fenton http://www.bway.net/~dfenton
dfenton at bway dot net http://www.bway.net/~dfassoc

Nov 12 '05 #15

This discussion thread is closed

Replies have been disabled for this discussion.