Why is tightly coupled bad but strongly typed good?
Asked Answered
P

9

13

I am struggling to see the real-world benefits of loosely coupled code. Why spend so much effort making something flexible to work with a variety of other objects? If you know what you need to achieve, why not code specifically for that purpose?

To me, this is similar to creating untyped variables: it makes it very flexible, but opens itself to problems because perhaps an unexpected value is passed in. It also makes it harder to read, because you do not explicitly know what is being passed in.

Yet I feel like strongly typed is encouraged, but loosely coupling is bad.

EDIT: I feel either my interpretation of loose coupling is off or others are reading it the wrong way. Strong coupling to me is when a class references a concrete instance of another class. Loose coupling is when a class references an interface that another class can implement.

My question then is why not specifically call a concrete instance/definition of a class? I analogize that to specifically defining the variable type you need. I've been doing some reading on Dependency Injection, and they seem to make it out as fact that loose coupling better design.

Pedal answered 30/10, 2010 at 5:30 Comment(6)
one is not concerned with the other at all . . .Pruter
I'm not voting to close this question, but IMO you are steering very close to the "argumentative" line here; especially because you have already stated that you know exactly why code should be loosely coupled... but you simply disagree.Sanatory
I disagree because I feel that the concept is "make it flexible because you don't know what you need", but that is not how variable typing is seen.Pedal
@JLX... changing code to work with new variable types is usually pretty easy. Re-engineering large swaths of code because you need to make a change to how a particular algorithm works... not so much.Leveridge
Saying code should be loosely coupled is like saying cars should run on gas. But lo' and behold, some cars run on diesel. Zomg. Those are bad cars! Bad I say!!Exum
A loosely coupled car could more easily run on gas or diesel ;)Leveridge
E
14

First of all, you're comparing apples to oranges, so let me try to explain this from two perspectives. Typing refers to how operations on values/variables are performed and if they are allowed. Coupling, as opposed to cohesion, refers to the architecture of a piece (or several pieces) of software. The two aren't directly related at all.

Strong vs Weak Typing

A strongly typed language is (usually) a good thing because behavior is well defined. Take these two examples, from Wikipedia:

Weak typing:

a = 2
b = '2'

concatenate(a, b) # Returns '22'
add(a, b)         # Returns 4

The above can be slightly confusing and not-so-well-defined because some languages may use the ASCII (maybe hex, maybe octal, etc) numerical values for addition or concatenation, so there's a lot of room open for mistakes. Also, it's hard to see if a is originally an integer or a string (this may be important, but the language doesn't really care).

Strongly typed:

a = 2
b = '2'

#concatenate(a, b)     # Type Error
#add(a, b)             # Type Error
concatenate(str(a), b) # Returns '22'
add(a, int(b))         # Returns 4

As you can see here, everything is more explicit, you know what variables are and also when you're changing the types of any variables.

Wikipedia says:

The advantage claimed of weak typing is that it requires less effort on the part of the programmer than, because the compiler or interpreter implicitly performs certain kinds of conversions. However, one claimed disadvantage is that weakly typed programming systems catch fewer errors at compile time and some of these might still remain after testing has been completed. Two commonly used languages that support many kinds of implicit conversion are C and C++, and it is sometimes claimed that these are weakly typed languages. However, others argue that these languages place enough restrictions on how operands of different types can be mixed, that the two should be regarded as strongly typed languages.

Strong vs weak typing both have their advantages and disadvantages and neither is good or bad. It's important to understand the differences and similarities.

Loose vs Tight Coupling

Straight from Wikipedia:

In computer science, coupling or dependency is the degree to which each program module relies on each one of the other modules.

Coupling is usually contrasted with cohesion. Low coupling often correlates with high cohesion, and vice versa. The software quality metrics of coupling and cohesion were invented by Larry Constantine, an original developer of Structured Design who was also an early proponent of these concepts (see also SSADM). Low coupling is often a sign of a well-structured computer system and a good design, and when combined with high cohesion, supports the general goals of high readability and maintainability.

In short, low coupling is a sign of very tight, readable and maintainable code. High coupling is preferred when dealing with massive APIs or large projects where different parts interact to form a whole. Neither is good or bad. Some projects should be tightly coupled, i.e. an embedded operating system. Others should be loosely coupled, i.e. a website CMS.

Hopefully I've shed some light here :)

Exum answered 30/10, 2010 at 5:54 Comment(0)
H
7

The question is right to point out that weak/dynamic typing is indeed a logical extension of the concept of loose coupling, and it is inconsistent for programmers to favor one but not the other.

Loose coupling has become something of a buzzword, with many programmers unnecessarily implementing interfaces and dependency injection patterns -- or, more often than not, their own garbled versions of these patterns -- based on the possibility of some amorphous future change in requirements. There is no hiding the fact that this introduces extra complexity and makes code less maintainable for future developers. The only benefit is if this anticipatory loose coupling happens to make a future change in requirements easier to implement, or promote code reuse. Often, however, requirements changes involve enough layers of the system, from UI down to storage, that the loose coupling doesn't improve the robustness of the design at all, and makes certain types of trivial changes more tedious.

Huckleberry answered 15/5, 2014 at 18:28 Comment(0)
V
5

You're right that loose coupling is almost universally considered "good" in programming. To understand why, let's look at one definition of tight coupling:

You say that A is tightly coupled to B if A must change just because B changed.

This is a scale that goes from "completely decoupled" (even if B disappeared, A would stay the same) to "loosely coupled" (certain changes to B might affect A, but most evolutionary changes wouldn't), to "very tightly coupled" (most changes to B would deeply affect A).

In OOP we use a lot of techniques to get less coupling - for example, encapsulation helps decouple client code from the internal details of a class. Also, if you depend on an interface then you don't generally have to worry as much about changes to concrete classes that implement the interface.

On a side note, you're right that typing and coupling are related. In particular, stronger and more static typing tend to increase coupling. For example, in dynamic languages you can sometimes substitute a string for an array, based on the notion that a string can be seen as an array of characters. In Java you can't, because arrays and strings are unrelated. This means that if B used to return an array and now returns a string, it's guaranteed to break its clients (just one simple contrived example, but you can come up with many more that are both more complex and more compelling). So, stronger typing and more static typing are both trade-offs. While stronger typing is generally considered good, favouring static versus dynamic typing is largely a matter of context and personal tastes: try setting up a debate between Python programmers and Java programmers if you want a good fight.

So finally we can go back to your original question: why is loose coupling generally considered good? Because of unforeseen changes. When you write the system, you cannot possibly know which directions it will eventually evolve in two months, or maybe two hours. This happens both because requirements change over time, and because you don't generally understand the system completely until after you've written it. If your entire system is very tightly coupled (a situation that's sometimes referred to as "the Big Ball of Mud"), then any change in every part of the system will eventually ripple through every other part of the system (the definition of "very tight coupling"). This makes for very inflexible systems that eventually crystallize into a rigid, unmaintanable blob. If you had 100% foresight the moment you start working on a system, then you wouldn't need to decouple.

On the other hand, as you observe, decoupling has a cost because it adds complexity. Simpler systems are easier to change, so the challenge for a programmer is striking a balance between simple and flexible. Tight coupling often (not always) makes a system simpler at the cost of making it more rigid. Most developers underestimate future needs for changes, so the common heuristic is to make the system less coupled than you're tempted to, as long as this doesn't make it overly complex.

Vinni answered 14/5, 2013 at 10:22 Comment(3)
(I feel I need to add a Post Scriptum to this: loose coupling is not the only way to lower the cost of changes. Techniques like Test-Driven Development tend to result in systems that are very simple, and hence relatively cheap to change, while still doing the job they need to do today. I'm answering on the assumption that you're doing traditional up-front design, also called "trying to predict the future". Once you switch to pure TDD, the simple/flexible dilemma becomes mostly a moot point.)Vinni
"as long as this doesn't make it overly complex" is the key thing to keep in mind. How do you know when a design is overly complex? In my experience, experience is the best guide :)Huckleberry
Tests can also add coupling and unit tests that test for concrete values are worse than say intergration or end-to-end tests at this, but stuff like tests & types & comments & constraint/contracts are considered part of your specification, you have to make sure that your specification is open to all possible use cases, while being easy to use for the common use case.Ralphralston
P
2

If you know what you need to achieve, why not code specifically for that purpose.

Short answer: You almost never know exactly what you need to achieve. Requirements change, and if your code is loosely coupled in the first place, it will be less of a nightmare to adapt.

Phonetics answered 30/10, 2010 at 5:37 Comment(4)
But really, isn't it "You almost never know exactly what you need to achieve in the end"? How does making something loosely coupled make it easier to adapt? That's like saying, "this variable should be a string, but since I can't know for sure, better make it untyped".Pedal
Now you're assuming there is an end ;)Phonetics
The answer is that if you're thinking strongly typed != loosely coupled, you're thinking on much too small a scale to understand why loosely coupled is good.Phonetics
Well for the string thingy if you had tests you would see that in the test it took a string, but a string is just one possibility. Type inference could also tell you hey this variable is used in text parameters.Ralphralston
L
2

Strongly typed is good because it prevents hard to find bugs by throwing compile-time errors rather than run-time errors.

Tightly coupled code is bad because when you think you "know what you need to achieve", you are often wrong, or you don't know everything you need to know yet.

i.e. you might later find out that something you've already done could be used in another part of your code. Then maybe you decide to tightly couple 2 different versions of the same code. Then later you have to make a slight change in a business rule and you have to alter 2 different sets of tightly coupled code, and maybe you will get them both correct, which at best will take you twice as long... or at worst you will introduce a bug in one, but not in the other, and it goes undetected for a while, and then you find yourself in a real pickle.

Or maybe your business is growing much faster than you expected, and you need to offload some database components to a load-balancing system, so now you have to re-engineer everything that is tightly coupled to the existing database system to use the new system.

In a nutshell, loose coupling makes for software that is much easier to scale, maintain, and adapt to ever-changing conditions and requirements.

EDIT: I feel either my interpretation of loose coupling is off or others are reading it the wrong way. Strong coupling to me is when a class references a concrete instance of another class. Loose coupling is when a class references an interface that another class can implement.

My question then is why not specifically call a concrete instance/definition of a class? I analogize that to specifically defining the variable type you need. I've been doing some reading on Dependency Injection, and they seem to make it out as fact that loose coupling better design.

I'm not really sure what your confusion is here. Let's say for instance that you have an application that makes heavy use of a database. You have 100 different parts of your application that need to make database queries. Now, you could use MySQL++ in 100 different locations, or you can create a separate interface that calls MySQL++, and reference that interface in 100 different places.

Now your customer says that he wants to use SQL Server instead of MySQL.

Which scenario do you think is going to be easier to adapt? Rewriting the code in 100 different places, or rewriting the code in 1 place?

Okay... now you say that maybe rewriting it in 100 different places isn't THAT bad.

So... now your customer says that he needs to use MySQL in some locations, and SQL Server in other locations, and Oracle in yet other locations.

Now what do you do?

In a loosely coupled world, you can have 3 separate database components that all share the same interface with different implementations. In a tightly coupled world, you'd have 100 sets of switch statements strewn with 3 different levels of complexity.

Leveridge answered 30/10, 2010 at 5:43 Comment(9)
Maybe I'm misinterpreting loosely coupled. I see this as, "instead of directly referencing an object to do a particular task, I am going to create an interface that my object can implement. That way I can implement the interface in any way and not be tightly coupled to it." I don't see how making this an interface instead of a direct class reference makes it any easier to adapt.Pedal
Strong typing != static typing, see here.Basset
@Zoul... no, strong typing != static typing, but strong typing == type restrictions that are enforced by the compiler.Leveridge
@Gerald: your db example assumes that you either have 100 different places where you're repeating your db call ("uncoupled" for lack of a better term) OR you can use an interface (loosely coupled). It doesn't talk about just concretely referencing a single class to call the query (strongly coupled).Pedal
@Gerald: also, if I had to use three different dbs in my code, I would prefer referencing three different db classes in each of those areas. Otherwise, I would have no real idea which db to look into to see where my data was being written to--I would only know that it was written to "some database".Pedal
@JLX... of course you would know. The application isn't just going to pick an implementation at random, you have to define which implementation it's going to use some way; probably through a configuration file.Leveridge
@JLX... if your introduction to loose- and tight-coupling is through reading about Dependency Injection, I could possibly see the confusion. Dependency Injection is on the extreme side of loose-coupling. Loose- and tight-coupling are relative terms though. When you are "concretely referencing a single class", that is more loosely-coupled than stringing a bunch of low-level database function calls throughout your code. If that concrete class has a more generic interface that isn't specific to it's internal implementation, that is looser still.Leveridge
@Gerald: Perhaps that's where my confusion is. I was attributing loose coupling solely to DI, because I've seen DI folks use it as a main reasoning behind that design.Pedal
@JLX... it is. And it's not a bad premise. Following in the database example, in DI the coupling is often so loose that you can create new database implementations without even re-compiling the rest of the application. In many cases you don't even need to shut the application down, you can just add a new DLL and make a change in a configuration file. That can be overkill for many applications, and isn't necessarily a good design idea in all cases, but it is in some. Generally speaking, the looser the coupling, the more flexible are your options for adapting, scaling and maintaining the code.Leveridge
L
1

Yet I feel like strongly typed is encouraged, but loosely coupling is bad.

I don't think it is fair to say that strong typing is good or encouraged. Certainly lots of people prefer strongly typed languages because it comes with compile-time checking. But plenty of people would say that weak typing is good. It sounds like since you've heard "strong" is good, how can "loose" be good too. The merits of a language's typing system isn't even in the realm of a similar concept as class design.

Side note: don't confuse strong and static typing

Levania answered 30/10, 2010 at 5:55 Comment(1)
I came here after noticing the term "weakly typed" in the JavaScript wiki on Stack Overflow. The term "weakly typed" seems a bit unfair to me. It's inherently making a judgment that weak typing is bad, which it is in some scenarios. It can be useful in others. I think the term "loosely typed" is a bit more neutral, even if it's a bit odd juxtaposed to "strongly typed."Pelasgian
S
1

strong typing will help reduce errors while typically aiding performance. the more information the code-generation tools can gather about acceptable value ranges for variables, the more these tools can do to generate fast code.

when combined with type inference and feature's like traits (perl6 and others) or type classes (haskell), strongly typed code can continue to be compact and elegant.

Sorayasorb answered 30/10, 2010 at 6:19 Comment(0)
T
0

If any modification done in our function, which is in a derived class, will change the code in the base abstract class, then this shows the full dependency and it means this is tight coupled.

If we don't write or recompile the code again then it showes the less dependency, hence it is loose coupled.

Topazolite answered 10/4, 2012 at 5:17 Comment(0)
I
0

I think that tight/loose coupling (to me: Interface declaration and assignment of an object instance) is related to the Liskov Principle. Using loose coupling enables some of the advantages of the Liskov Principle.

However, as soon as instanceof, cast or copying operations are executed, the usage of loose coupling starts being questionable. Furthermore, for local variables withing a method or block, it is non-sense.

Indignity answered 2/9, 2012 at 12:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.