In other words, the above code is semantically no different than 'obj.GetSomething().DoSomething()', which violates the double-dot rule.
As said, languages used in practice do not make it viable to follow such strict principles. Here is a well-designed example:
SqlQuery q = SqlQuery.select("*").from("people").where("id == 100");
Plenty of dots, but no problem.
Violation of law comes from making assumptions about structure of called objects. Here we always call on same *interface*, so the depth remains one.
When doing somehting like:
we make assumptions about engine having torque. This point is blurred due to static typing in most languages. Since we can do compile-time check we know that engine has torque, so everything is seemingly fine. IDEs make a mockery of this even further.
But let's consider a dynamic language or even SmallTalk. getEngine() may return something with torque, something without torque or a cheeseburger. OO does not require strict or static or any kind of typing.
This statement is fine - eval makes something, then takes torque. But counter example would be:
Suddenly we simply guess that an engine has torque, which breaks encapsulation. If we were to make such as assumption, then it would need to be getEngineTorque().
In pure OO, everything is object, so eval could return "1", "func()", instance of Engine, instance of RevvedEngine, torque or text. No assumption of anything can be made, unlike common languages today where return would be typed as IEngine, which obviously has getTorque() engine.
Code completion is partly to blame for this since it encourages deep calls resolved during some compile-time declaration. Dynamic languages are, ironically, better at OO than those commonly touted as OO due to lack of such tooling which doesn't lead to degenerate design.
In same vein, casting exhibits same flaws. One should never cast to gain access or use instanceof/typeof/etc... to try to force or guess existence of some trait. If Engine doesn't have getTorque(), but RacingEngine does, casting should be avoided (unless if required to work around language limitations, which is the norm in Java and most everywhere else). But design or implementation that requires such behavior is poorly designed.