What do People do to avoid Tight Coupling?

  • Thread starter Thread starter Charles Law
  • Start date Start date
C

Charles Law

Take a solution with a project hierarchy along the lines of an n-tier
system, so that we have a data layer, business layer and presentation layer.
The presentation layer is coupled to the business layer, and the business
layer is coupled to the data layer. So far so good.

Suppose the data layer raises an event, and it passes Me (the sender) as an
object, and e (MyEventArgs, a descendent of EventArgs) to the layer above
(the business layer). Suppose also that the business layer needs to pass
this event on to the presentation layer. It sends Me as an object, but what
does it send e as? It can't send it as MyEventArgs because the presentation
layer knows nothing of such things. We could couple the presentation layer
to the data layer, so that it knows what a MyEventArgs is, but surely that
is a no-no.

We could also remove the definition of MyEventArgs to another project to
which everything is coupled, but now this common project is in danger of
being an eclectic mix of stuff that is really specific to individual
projects.

We could define a business layer version of MyEventArgs - which is actually
identical to the one in the data layer - and copy each element to the new
object before passing it on, but that is a lot of typing, and now we have
duplication.

I am interested to hear what other people do in this situation.

Charles
 
Charles,

Rather then talking in the abstract what specifically does the presentation
layer need to react to in conjunction with the data layer?

Dan
 
Suppose the data layer raises an event, and it passes Me (the sender) as
an
object, and e (MyEventArgs, a descendent of EventArgs) to the layer above
(the business layer).

Perhaps you should stop there. Rather than passing an object (or more
specifically itself) back to the business layer, why not pass either a
different object or just data to the business layer for it to consume?
 
Charles,
The presentation layer is coupled to the business layer, and the business
layer is coupled to the data layer. So far so good.
Actually I normally couple the presentation layer to the domain layer
(business layer) and couple the data layer to the domain layer!

See Martin Fowler's book "Patterns of Enterprise Application Architecture"
from Addison Wesley
http://www.martinfowler.com/books.html#eaa. The sections on Domain Model,
Data Mapper, and Separated Interface discuss how to have the data layer
reference the domain layer, instead of having the domain layer reference the
data layer. Having the data layer reference the domain layer, allows
replacing the data layer very easily! Also I find it provides better
separation (less coupling)...
Suppose the data layer raises an event, and it passes Me (the sender) as an
object, and e (MyEventArgs, a descendent of EventArgs) to the layer above
In one of my projects that data layer has custom event handlers
(MyEventArgs, a descendent of EventArgs), while the domain layer simply uses
EventHandler & passes EventArgs.Empty. As I found that the domain layer
needed to convey less information to the domain layer, then the data layer
did to the domain layer.
We could define a business layer version of MyEventArgs - which is actually
identical to the one in the data layer - and copy each element to the new
object before passing it on, but that is a lot of typing, and now we have
duplication.
I suspect most of the time the info that the domain layer needs in its
events is going to be different then the info needed in the data layer's
events, so each would have its own event handlers...

Hope this helps
Jay
 
Hi Scott

I used the (sender As Object, e As MyEventArgs) style to follow the pattern
proposed by Microsoft. MyEventArgs has properties that give access to the
data that it is to be consumed.

The situation I am describing is when that data needs to be passed on to the
layer above. The straight forward way seemed to be just to bubble the event,
but then the layer above needs to know what a MyEventArgs class looks like.

Charles
 
Hi Dan

I used the 3-tier example because it is a particularly familiar one. My
scenario has a presentation layer on top of a command layer, on top of a
communications layer, but with other bits on the side. It's a bit hard to
describe really.

The presentation layer has view windows containing indicator controls. These
controls are held in a library project. The presentation layer is
responsible for instantiating these controls in a view window.

The command layer gets data back from the comms layer and raises events
based on the information. These events are handled by the indicators, which,
whilst being instantiated at the presentation layer are contained in the
library, so that is where the events are handled. What this means is that
the library is coupled to both the presentation layer and the command layer,
and the presentation layer is coupled to the command layer. This doesn't
quite create a circular reference, but it does cause the .NET IDE/compiler
immense problems when building.

So, my reason for asking the question has other implications in that if I
can reduce the coupling I will actually be solving my build problems.

Charles
 
Hi Jay
I suspect most of the time the info that the domain layer needs in its
events is going to be different then the info needed in the data layer's
events, so each would have its own event handlers...

I agree that this the case most of the time, it's just that in my case it
isn't. If I used different EventArgs classes I would just be changing the
object for the sake of it. No information is added or omitted as the event
passes up the layers.

As I described in my response to Dan, I have a set of custom controls that
raise and sink events. These are instantiated by the presentation layer, but
the command layer is where the corresponding events are sunk (sinked?) and
raised. I have ended up with tighter coupling than the compiler/IDE is
comfortable with, and I get random, erroneous build errors all the time. I
have seen others post about this problem, and it comes and goes for me. At
the moment it has come, and I can't build my solution because I get large
numbers of spurious messages about handlers and events having different
signatures, when they haven't. That's why I am trying to loosen the coupling
so that I can just build the thing.

Charles
 
Charles,

I do a simlar thing but without events and do not have a problem. It sounds
to me like you need to start defining some interfaces and remove the events.
Doing this should remove some of you coupling problems and perhaps increase
your performance.

Indicator control supports INotify interface

Your presentation layer instantiates
(1) the indicator control
(2) the command layer.
(3) initialzes the command layer with the already instantiated indicator
control(s) using the INotify interface

As the command layer as you say "gets data back from the comms layer"
instead of raising events it could loop through a set of registered objects
supporting the INotify interface and call an appropriate method.

In this pattern, the indicator control knows nothing about your presentation
layer, command layer or communication layer. The coupling looks something
like this (where -> means "knows" or "is coupled to")

Presentation -> Indicator
Presentation -> Command
Presentation -> INotify

Indicator -> INotify

Command -> INotify
Command -> Comms

Dan
 
Hi Dan

Thanks for the time you have obviously spent understanding what I am trying
to do. I have used interfaces, but what has stopped me going that extra bit
further is the fact that I won't be able instantiate an interface. So, I
have settled with coupling to the library that contains the class itself.

I like the look of this INotify interface though. The name is suggestive of
the sort of thing I should be doing, so I am going to look into it further
and then let it sink in.

Thanks again.

Charles
 
Charles,
It sounds like you really need to learn the Separated Interface Pattern
(Martin Fowler's book).

http://www.martinfowler.com/eaaCatalog/separatedInterface.html

Define an Interface that Class1 uses that Class2 implements. Put this
interface in the same assembly as Class1 (or a third assembly). The Class1
assembly needs to reference the assembly where the interface is defined if
its not in the same assembly. The Class2 assembly needs to reference the
Class1 assembly & the assembly where the interface is defined. Class2 can
reference Class1 directly, while Class1 can only reference Class2 via the
interface that it implements.

Something like:
' Assembly 1
Public Interface IClass2
Public Property Value1() As Integer
Public Sub Method1()
End Interface

Public Class Class1

Public Sub Test(ByVal object2 As IClass2)
If object2.Value1 = 100 Then
object2.Method1()
End If
End Sub

End Class

' Assembly 2
' References Assembly 1

Public Class Class2
Implements IClass2

Public Property Value1() As Integer Implements IClass2.Value1
...

Public Sub Method1() Implements IClass2.Value1
...

End Class

Note instead of an Interface, you could use an Abstract Base Class
(MustInherit).

The Interface IClass2 can contain events in addition to Properties,
Functions & Subs.

Also to elimate the coupling between Presentation & Data, I would use a
Proxy object for the EventArgs instead of actually copying the data.

Something like:

' From Data layer
Public Class DataEventArgs
Inherits EventArgs

Private Readonly m_amount As Integer

Public Sub New(amount As Integer)
m_amount = amount
End Sub

Public Readonly Property Amount As Integer
Get
Return m_amount
End Get
End Property

End Class

' From Domain Layer

Public Class DomainEventArgs
Inherits EventArgs

Private Readonly m_dataEventArgs As DataEventArgs

Public Sub New(dataEventArgs As DataEventArgs)
m_dataEventArgs = dataEventArgs
End Sub

Public Readonly Property Amount As Integer
Get
Return m_DataEventArgs.Amount
End Get
End Property

End Class

Although the Domain layer duplicates all the properties, the data itself is
not duplicate or exposed, as the DomainEventArgs acts as a Proxy for the
DataEventArgs, delegating all the calls to the DataEventArgs...

Hope this helps
Jay
 
Hi Jay

I don't have the Fowler book, but I can feel a purchase or two coming on. I
have the GoF Design Patterns book, which does not mention the Separated
Interface Pattern as such; perhaps under a different name (?).

I have used interfaces in much the way you describe, so I am reassured in
that respect.

With regard to the use of a proxy, it does save copying the data, but with
around 50 events being bubbled, each with their own EventArgs class, that
still means another 50 proxy classes.

Just to clarify, you say 'assembly', I say 'project'. Are we talking about
the same thing?

Charles
 
Charles,
I don't have the Fowler book, but I can feel a purchase or two coming on. I
have the GoF Design Patterns book, which does not mention the Separated
Interface Pattern as such; perhaps under a different name (?).
Fowler's book takes off where the GoF book stops.
I have used interfaces in much the way you describe, so I am reassured in
that respect.
I may have miss read one of your other posts, I got the impression you did
not understand interfaces...
With regard to the use of a proxy, it does save copying the data, but with
around 50 events being bubbled, each with their own EventArgs class, that
still means another 50 proxy classes.
It really comes down to: How much coupling are you willing to live with! ;-)

The proxy allows you to avoid coupling, have fair performance, and expose
all or most of the same properties in both events. At the expense of
creating the proxy classes...
Just to clarify, you say 'assembly', I say 'project'. Are we talking about
the same thing?
Yes

Hope this helps
Jay
 
Hi Jay
I may have miss read one of your other posts, I got the impression you did
not understand interfaces...

Not me guv. You must have me mixed up with someone else; use 'em all the
time, I do ;-)

Anyway, I take on board what you say. Thanks.

Charles
 
Charles,
Earlier in this thread you replied to solex with:
Thanks for the time you have obviously spent understanding what I am trying
to do. I have used interfaces, but what has stopped me going that extra bit
further is the fact that I won't be able instantiate an interface. So, I
have settled with coupling to the library that contains the class itself.

The "won't be able instantiate an interface" is the cause of my confusion
(where I got the impression...).

When I use the Separated Interface Pattern, I normally use the Plugin
Pattern also.
http://www.martinfowler.com/eaaCatalog/separatedInterface.html
http://www.martinfowler.com/eaaCatalog/plugin.html

I store the names of the classes that implements a specific interface in the
app.config file. I then use System.Activator.CreateInstance to create
instances of these classes. Sometimes I simply pass the interface as a
parameter as I show in my earlier post.

For example in the Domain Model & Data Mapper scenario I mentioned earlier,
I have a custom configuration section in my app.config that identifies the
data mapper class for a given domain class. In my framework assembly I have
a shared method that takes a domain object Type and looks up the DataMapper
type name in the app.config, uses Activator.CreateInstance to return an
instance of the class that implements IDataMapper.

I can post code samples this evening if you like.

Hope this helps
Jay
 
Aah. All I meant was that I wouldn't be able to write

Dim c1 As New IClass1

To instantiate I would need to have a reference to the 'assembly' (good,
eh?) containing Class1.

Yes, I would be interested to see an example of the Domain Model / Data
Mapper scenario, if it's not too much trouble.

Charles
 
Hi Dan

Thanks for the reference. I'm not sure which bit I mis-typed:
to do. I have used interfaces, but what has stopped me going that extra
bit further is the fact that I won't be able [to] instantiate an interface. So, I
have settled with coupling to the library that contains the class
itself.

apart from missing out the word 'to'.

Charles
 
Charles,
Dim c1 As New IClass1
You would need to instantiate a class that implements the interface, you can
use Activator.CreateInstance to avoid directly coupling to the assembly
where class is. You would still need to couple to the assembly where the
interface is.
Yes, I would be interested to see an example of the Domain Model / Data
Mapper scenario, if it's not too much trouble.

This is a partial implementation of the Domain Model pattern along with the
DataMapper pattern & Finder "pattern" from Fowlers book. It should be enough
to get you started.

' within MyApp.Framework.dll

Public Interface IDataMapper
Sub Insert(ByVal obj As IDomainObject)
...
End Interface

Public NotInheritable Class UnitOfWork

Public Shared Function GetDataMapper(ByVal domainType As Type) As
IDataMapper
Dim dataMappers As Hashtable =
DirectCast(System.Configuration.ConfigurationSettings.GetConfig("dataMappers
"), Hashtable)
Dim typeName As String =
DirectCast(dataMappers(domainType.Name), String)
Dim mapperType As Type = Type.GetType(typeName)
Dim mapper As IDataMapper =
DirectCast(Activator.CreateInstance(mapperType), IDataMapper)
Return mapper
End Function

...

End Class

Public Class DataMapperSectionHandler
Inherits System.Configuration.DictionarySectionHandler

Protected Overrides Readonly Property KeyAttributeName() As String
Get
Return "name"
End Get
End Property

Protected Overrides Readonly Property ValueAttributeName() As String
Get
Return "dataMapper"
End Get
End Property

End Class

' within MyApp.Data.dll
' References MyApp.Framework.dll
' References MyApp.Domain.dll

Public Class PublisherMapper
Implements IDataMapper
Implements IPublisherFinder

Public Sub Insert(ByVal obj As IDomainObject) Implements
IDataMapper.Insert
End Sub

...

Public Function FindById(ByVal id As Integer) As Publisher
Implements IPublisherFinder.FindById
' do lookup of id
Return New Publisher(id, name, address, ...)
End Class

' within MyApp.Domain.dll
' References MyApp.Framework.dll

Public Interface IPublisherFinder
Function FindById(ByVal id As Integer) As Publisher
Function FindByName(ByVal name As String) As Publisher
End Interface

Public Class Publisher

Public Shared Readonly Property Mapper() As IDataMapper
Get
Dim mapper As IDataMapper
mapper = UnitOfWork.GetDataMapper(GetType(Publisher)
Return mapper
End Get
End Property

Public Shared Readonly Property Finder() As IPublisherFinder
Get
Dim mapper As IDataMapper
mapper = UnitOfWork.GetDataMapper(GetType(Publisher)
Return DirectCast(mapper, IPublisherFinder)
End Get
End Property

End Class

' within app.config or web.config depending on main.exe

<configuration>
<configSections>
<section name="dataMappers"
type="Myapp.Framework.DataMappersSectionHanlder, MyApp.Framework" />
</configSections>
<dataMappers>
<add name="Publisher" dataMapper="MyApp.Data.PublisherMapper,
MyApp.Data" />
...
</dataMappers>
</configuration>

Hopefully I included enough for you to get the full jest of it! ;-)

Hope this helps
Jay

Charles Law said:
Aah. All I meant was that I wouldn't be able to write

Dim c1 As New IClass1

To instantiate I would need to have a reference to the 'assembly' (good,
eh?) containing Class1.

Yes, I would be interested to see an example of the Domain Model / Data
Mapper scenario, if it's not too much trouble.

Charles
<<snip>>
 
Charles,
Oh! Making Finder a shared property of the Publisher class allows:

Dim aPublisher As Publisher = Publisher.Finder.FindById(100)

When I first starting reading/thinking about the Finder pattern, I wanted to
make each of the IFinder methods as shared on the Domain object, but was
thinking that would be too much work ;-)

Of course you can adopt names to fit your style...

Hope this helps
Jay
 
Back
Top