开发者

Why is tightly coupled bad but strongly typed good?

开发者 https://www.devze.com 2023-01-23 06:12 出处:网络
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

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 readi开发者_StackOverflow中文版ng 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.


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 :)


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.


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.


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.


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.


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


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.


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号