It is tough to describe but I've become rather tired of object oriented design. Part of this is because I'm not very good at it. But a larger part is because it seems to muddle two important aspects into one. I'm talking about data and functions of course. The idea makes sense to replicate real world "object" in code and have those object interact. The model sounds so elegant and correct, one can't help but believe it is the right way to go. Yet, in practice, it just gets frustrating.
Folks who know languages like SmallTalk probably have a different take. While I've never done anything with the language, my understanding is that OO in practice broke some very important constructs that made SmallTalk as a language very nice to program with. Specifically, the idea of message passing was meant to be the intended means of interacting with objects.
This small aspect might be just the reason OO has never really sat well with me. Passing messages makes more sense beca use you have an object that provides functionality, but interacts via messages of data. There no longer is a dependency on some object's attribute and any API immediately becomes more stable through the assumptions necessary passing messages. This is part of the reason I really liked WSGI and why functional programming seems so powerful.
When you mix your data in your objects you remove a level of abstraction that is actually helpful. I realize in Java the whole getter and setter pattern is pretty abysmal, but that is because the idea is flawed. The purpose of OO is to make code more reliable by isolating data and cohesively organizing functionality. Things like getters and setters are wrong because they are isolating the data at the language level. As folks in the Python world would argue, that is silly, we're all adults. But, as someone designing an application a more effective means of hiding or protecting data is to use message passing. Just to be clear, in my mind I simply see message passing as calling functions where the message is the set of arguments. You can go in two directions with that by using a dictionary or set of arguments. In this way you can define how lenient your contract is between two objects. If you need a rather strict interface, declare every argument, otherwise, passing in dictionary that is the "message" might be helpful if the object only needs to act on one part of it.
The other helpful aspect of this is that you remove much of the potential confusion found by things like operator overloading. This is very different from designing a model object that acts like a native object or has certain attributes that are dynamically created. This is a natural abstraction that makes sense. Where it doesn't make sense is in objects that are primarily there to perform actions. It is very similar to performing operation in a web application based on GET parameters. This is a bad idea because the understanding is that the operation is idempotent and safe. One would think that accessing a n attribute is safe, but if that attribute does some set of operations, then it is better to use a function so it is clear. Likewise, using actual functions for functionality helps describe the operation in the same way hyperlinks help on the web. You can easily find that function and see what is happening. The more the isolated, the less hunting around in the different objects you have to do to find what some attribute might be.
None of this suggests you shouldn't use objects. It is just making a clear distinction that if an object has a lot of data, it might be best to act on that data via some other object or function. You then have the opportunity to keep things easy to follow and find where something is going wrong. It acts like a map of a code path that helps narrow down where something is broken. This lets you test and isolate issues more effectively and keep the cognitive requirements much lower.