I went little fancy and wrote Selenium page-object with Java 8 streaming as mentioned in below code and got a review comment that my code is breaking Law of Demeter, since I am doing lot of operations in a single line. I was suggested to break the code to first stream to collect to list and run another stream operation to do match( in short break it down into multiple streams as needed). I was not convinced as Stream is introduced for handling data processing and if we break it down into multiple streams, there is no point using stream. Previously I have worked for a cyber security project, where millions of records were processed with streaming with multiple logic operations to sort the data.
Please share your thoughts, I have changed it as the reviewer suggested but he couldn't explain why and I want to learn more about streams and right way of utilizing this powerful addition to java 8.
Here is the sample code:
listOfWebElements.stream().filter(element -> element.getText().contains(name)).findFirst().ifPresent(match -> match.click());
is line I am referring to in this question, providing the method so that it makes more sense.
@FindBy(css = "#someTable td.some-name li a") List<WebElement> listOfWebElements;
public SomeClass doSomething(String name) {
wait.until(visibilityOfAllElements(listOfWebElements));
listOfWebElements.stream().filter(element -> element.getText().contains(name)).findFirst()
.ifPresent(match -> match.click());
return new SomeClass(driver);
}
Java 8 streams are an example of a fluent interface, and are intended to allow writing in a functional programming style. There is a lot of disagreement over what breaks the LoD and whether it even matters, but all that aside, the example in the documentation for Stream
shows that you are using it as intended by the Java language designers.
If that isn't enough for your reviewer, note that the purpose of the Law of Demeter (aka the principle of least knowledge) is to keep programs loosely-coupled by minimizing the number of classes that a class directly communicates with. When A
has a B
and B
has a C
, and you want A
to make C
do something, you should have it tell B
to get it done and let B
worry about the details of how and when C
is used.
In this case, every intermediate method on Stream
is returning another instance of Stream
, so you are still only coupled to the Stream
class. I would not consider this a Law of Demeter violation.
I would also say that any class in a language should be considered coupled to the language's standard library. Therefore, any standard library objects should be exempt from the Law of Demeter, since you can't uncouple from them anyway. Otherwise, you wouldn't even be able to call get
on a List
that was returned by an object, or deal with a Set
of Map.Entry
from Map.entrySet()
. This would also cover Stream
, of course.
I was suggested to break the code to first stream to collect to list and run another stream operation to do match( in short break it down into multiple streams as needed).
Storing intermediate objects in local variables does not fix a Law of Demeter violation, you would still be accessing objects returned by other objects. It sounds like your reviewer is just blindly dot-counting.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With