56

This is easy. Almost nothing matters more than clarity to the reader. The first variant I found incredibly simple and clear. The second 'improved' version, I had to read several times and make sure all the edge conditions were right. There is ZERO DOUBT which is better coding style (the first is much better). Now - what is CLEAR to people may vary from ...


27

The notion of invariant is strongly linked with 'side effects'. I believe it was promoted by Bertrand Meyer's 'Design by Contract (DbC)' approach for software design. DbC enriches Abstract Data Types (backbone of classes) with 3 important notions, preconditions, postconditions, invariants. It is easily explained when referring to procedures, so I'll try to ...


20

It's usually bad form in C++ to create zombie objects that explode if you touch them. But that's not what you're doing. You're creating a "zombie object" that will explode if you touch it wrongly. Which is ultimately no different than any other state-based precondition. Consider the following function: void func(std::vector<int> &v) { v[0] = 5;...


19

I think for simple loops, such as these, the standard first syntax is much clearer. Some people consider multiple returns confusing or a code smell, but for a piece of code this small, I do not believe this is a real issue. It gets a bit more debatable for more complex loops. If the loop's contents cannot fit on your screen and has several returns in the ...


16

A loop invariant is simply something that is true on every iteration of the loop. For example, take a really trivial while loop: while x <= 5: x = x + 1 Here the loop invariant would be that x ≤ 6. Obviously, in real life, loop invariants are going to be more complicated--finding the loop invariant in general is something of an art and cannot easily ...


14

Well, the stuff I'm seeing in this thread is all great, but I have a definition of an 'invariant' that has been tremendously helpful for me at work. An invariant is any logical rule that must be obeyed throughout the execution of your program that can be communicated to a human, but not to your compiler. This definition is helpful because it cleaves out ...


11

Variance has nothing to do with mutability. It appears at the intersection of parametric polymorphism and subtyping. The oldest example of variance is actually an example that comes out of functional programming, namely the question: when is a function type a subtype of another function type? And it turns out that a function type is a subtype of another ...


10

The need for covariance and contravariance can be best understood with an example. Let's say you have a function that accepts a parameter of type List<Base>, where Base is a base class that other classes inherit from. You would intuitively think that it would be just fine to pass a List<Derived1> to this routine, if Derived1 inherits from Base, ...


9

int i = 0; while (i < array.length && array[i] != value) i++; return i < array.length; […] everything is more obvious and more in a structured-programming way. Not quite. The variable i exists outside the while loop here and is thus part of the outer scope, while (pun intended) x of the for-loop exists only within the scope of the ...


9

does even the concept of variance make sense in an immutable environment? Yes; presence of subtyping is independent of immutability. could every type of a speculative fully immutable OOP language (like, all of them) be made covariant, regardless of where they are used (i.e. as function input types / function output types)? As a simple demonstration why ...


8

An invariant is a condition that can be relied upon to be true during execution of a program. For example, a loop invariant is a condition that is true at the beginning and end of every execution of a loop. An assertion is a predicate (a true–false statement) placed in a program to indicate that the developer thinks that the predicate is always true at that ...


7

An algorithm is a repeatable process. If it is repeatable, it has to have attributes that do not change with repetition. These are your invariants. The invariants are combined with and/or operate on the (potentially) varying data that will be fed into your algorithm. Thus the whole point of programming is to identify what does not vary--that is ...


7

How could they have prevented this? (Is this calling for a technical solution or is it a question of people and best practices?) First things first, the ambiguity could possibly have been avoided with a better model. A rectangle can be represented as a pair (x, y) (or (top, left) to further reduce the ambiguity), a width and a height. If one sticks with ...


6

Objects are responsible for preserving the integrity (e.g.,some invariant) of the data representation. An invariant is just something that needs to stay true at all times. For example, if you have an object that represents a range of some sort, and stores a lower limit (let's call it a) and an upper limit (b), an invariant would be the requirement that a ...


6

Choose solution #1. There is nothing wrong with setters containing validation or coercion logic. In fact, that is the only reason why we'd use setters and getters instead of public member fields! Your create factory method is also a bit awkward, as there is no reason not to use the constructor instead: public class UserCredentials { private String ...


5

Encapsulation is not a feature that came with OOP. Any language that supports proper modularization has it. Here's roughly how you do it in Haskell: -- Rational.hs module Rational ( -- This is the export list. Functions not in this list aren't visible to importers. Rational, -- Exports the data type, but not its constructor. ratio, ...


5

You are definitely in the right here. This is pretty basic defensive programming and there are no real arguments against it. Now for the arguments : The coupling to IoC and fail-fast issue is just like you said. As for the third argument, just because this one class has argument null check doesn't mean you have to go back and change all other classes. It ...


4

Why those are considered as invariants. ? Because they're the intrinsic things that define this sort of architecture. If you allow shared state or knowledge of filters, you're no longer doing Pipe and Filter, you're doing something else. The entire point of this architecture is to create independent, composable, parallelizable streams of work. This allows ...


4

You do it the same way: make a constructor that enforces the constraint, and agree to use that constructor whenever you create a new value. multiply lhs rhs = ReducedFraction (lhs.num * rhs.num) (lhs.denom * rhs.denom) But Karl, in OOP you don't have to agree to use the constructor. Oh really? class Fraction: ... Fraction multiply(Fraction lhs, ...


4

I would go with an assertion. Without any further information, I'd probably default to the non-static assert(). A comment would indeed be wishful thinking. Not only can it be ignored, but it can very easily go out of date if (when?) you decide 32 should no longer be the limit. If this was a public API with other programmers using it, the exception would be ...


4

I only recently learned that I needed to have contravariant interface to be able to pass that interface as a parameter in C# and this feature was only added in .NET 4.0. .NET 4 added co/contra-variance for generics. The variance concept though exists in any language with subtyping. When doing type checking, a parameter type is acceptable if it is the same ...


3

Based on the following quote from Coders At Work... But once you know the invariant that it's maintaining, you can see, ah, if we maintain that invariant then we'll get log lookup time. ...I guess "invariant" = "condition you want to maintain to ensure a desired effect". It seems that invariant has two senses that differ in a subtle way: Something ...


2

An invariant is a logical property that is preserved by some operation(s). You need invariants to reason about loops. Since you don't know beforehand how many iterations there will be (or you wouldn't need a loop), each iteration must preserve the invariant, so that at the end you can prove some useful property about the loop. You need invariants to reason ...


2

Some functional languages such as OCaml have built-in mechanisms to implement abstract data types therefore enforcing some invariants. Languages which do not have such mechanisms rely on the user “not looking under the carpet” to enforce the invariants. Abstract data types in OCaml In OCaml, modules are used to structure a program. A module has an ...


2

I found a very good explanation, which includes an example of usage, here: http://www.cs.uofs.edu/~mccloske/courses/cmps144/invariants_lec.html The example with red and blue marbles in the jar totally explained the trick. I will try to summarise so that the answer complies with stack rules (some parts might be a copy-paste of the original). -Suppose ...


2

There's a fundamental principle about microservices that cannot be denied. Microservices are independent entities; if you are building a system that is based on microservices, then by definition you cannot have them depend on a central data authority (like a relational database), because your microservices would no longer be independent. Giving each ...


2

In a multi-tier application an application is usually divided into three layers: presentation, business logic, data access. A data access layer should do one thing and do it well, it should know how to retrieve and persist entities which are used by business logic layer and shouldn't contain any business logic. Note, that I am saying shouldn't, because ...


2

This is mostly a rewording of this excellent blog by Eric Lippert: C# sub types have always been assignment compatible with their base types e.g. given that Teacher is derived from Person Person p = new Teacher(); is valid i.e. there is a relation isAssignable(x,y) which is true IFF x= y is allowed. before C# 4 generic collections of sub types were not ...


2

The two loops have different semantics: The first loop simply answers a simple yes/no question: "Does the array contain the object I'm looking for?" It does so in the most brief manner possible. The second loop answers the question: "If the array contains the object I'm looking for, what is the index of the first match?" Again, it does so in the most brief ...


2

I'll suggest a third option altogether: return array.find(value); There are many different reasons to iterate over an array: Check if a specific value exists, transform the array into another array, calculate an aggregate value, filter some values out of the array... If you use a plain for loop, it's unclear at a glance specifically how the for loop is ...


Only top voted, non community-wiki answers of a minimum length are eligible