Preferences

Same story repeating over and over again.

Initial hype. Then abuse and overuse. Disillusionment. Decline. At some point people will finally figure out what OOP is all about by looking up decades' old works and youtube videos (if youtube is still available) and hopefully will learn to integrate it.

*

A discussion with a dev recently when I started refactoring his PR with him and started pulling business logic from services into model.

Dev: "NOOO!!! You can't do this!!!"

Me: "But why?"

"You can't put code in model. Model is for fields and getters and setters only and business logic belongs in business services"

Me: "Why?"

Dev: blank stare... (imagined, this was over zoom)

Me: "So... why do you bother with having fields private if you plan to have public getters and setters for everything?"

Dev: blank stare

Me: "Isn't object oriented programming about having logic alongside the data rather than separate? Isn't it about modeling operations that act on the data to limit exposure to internal state?"

Dev: blank stare

Me: sigh...


Just to set some context, I'm of the "OOP Generation" where I learned to program when OOP was at its zenith. Every major language was Class-based OOP (C++, C#, Java, Python, Ruby) and it was just THE way to code for all things in the foreseeable future.

That said, I've since looked into and learned other methods of programming: classic C, prototype-based JS, functional Clojure, and Go.

From what I can gather, even from the SOLID principles and other Class-based OOP advice (prefer composition over inheritence) is that Interfaces (as abstraction and reuse), Aggregation (that objects can have other objects), and Delegation (which allows of ergonomic aggregation) are the real winners of OOP.

In this way, Go gets it mostly right IMO, though at the risk of "initial hype" I'm still cautious on it.

It feels so backward that OOP is taught with a focus on abstract & subclasses, with very little emphasis on interfaces, which is really how we achieve the goals of abstraction and reuse.

What's funny is seeing languages like Java trying to "undo" the arguable "mistake" of abstract classes by adding more and more power into interfaces: default methods, etc.

To your original conversation, there's a mix going on with "model." Are they talking about the Service's API Model, the internal model representation, or the database model? The outer layers (API & Database) of your code should be plain structs (POJOs in Java), but you're absolutely right that within the business code itself, you should have logic within the classes. Otherwise, you're not coding much different than C passing around struct pointers.

Related terms: MVC, Data access object

I'm a Pascal programmer, so I come from a different heritage.... anytime I go to look at OOP in C++ or Java, etc... I see a fog of things about factories, and namespaces, and far, far too much code just to set up objects, which are glorified data structures with the code that manages it.

I strongly agree with your conclusion that Composition, Interfaces, Aggregation, and Delegation are the real value in OOP.

They all play very nicely together. Composition avoids the need to restructure everything into a single hierarchy.

Interfaces make it possible to separate concerns, which lets you treat each library as a black box.

Example: Imagine the electrical breaker panel in your home. The exact principles that cause the breaker to trip are irrelevant, nobody cares if it's thermal, magnetic, hydraulic, or whatever new tech comes down the block... all that matters is that it does the job in the described manner. It meets the goals of its interface.

In hiding implementation behind the interface, you fend off premature optimization, and stave off technical debt by keeping things small enough to refactor at will. It is this standardization that allows you to add a Ground Fault Interrupter to a box that was designed before they became available.

Aggregation is how you can have a series of fields in a form, all of different types, yet it all is a form. If one of those fields has a list of strings... nothing breaks, and you don't have to re-arrange everything to make it work.

Example: Your breaker panel has any mix of 120 and 240 and GFI breakers, some even do 3phase power.

Delegation is how the fields in a form let the details of where on the screen, and how to resize, just work.

Example: Your breaker panel has X number of slots, provides a UL approved housing for them, with a reliable User interface.

OOP isn't the problem, the teaching is.

> What's funny is seeing languages like Java trying to "undo" the arguable "mistake" of abstract classes by adding more and more power into interfaces: default methods, etc.

Default methods are how Java-like languages are able to have mixins. They are extremely handy and quite safe to use as far as I can tell.

golang on the other hand shows its extreme weakness in modeling non-trivial domains, and the code base quickly becomes very verbose and tedious to work in.

You need to read up on Anemic Domain Model (https://martinfowler.com/bliki/AnemicDomainModel.html).

I see a lot of chaos in your post.

Model is a model. Model is there to represent an aspect or aspects of a real system (https://en.wikipedia.org/wiki/Conceptual_model). An object called "Employee" is a "model", a representation of certain aspect of an actual employee.

Object oriented programming is about using objects -- models (representations of aspects) of actual systems, actors, objects, etc. In OOP you build the model from operations that define the object. The fields are not part of the interface, the operations are. That's why everybody says to make fields public (but for some reason not everybody remembers why).

So a method call fire() on an object Employee is more "object oriented" than having HREmployeeService.fireEmployee(employeeId) to set field employedTo directly on EmployeeDAO.

I think OOP is great, but one weakness is that a lot of abstract concepts don't really map to objects. And then people have flamewars about where to put those concepts.

In the real world most employees would never fire themselves and HR would update things like payroll which the employee isn't allowed to change. Your example makes more sense as a justification for keeping a service layer.

Abstract concepts map very well to objects. The problem of mapping a business domain to computing structures isn't unique to OOP.

In this case, I'm slightly gobsmacked that no-one pointed out that an employee is not their job. The contract of employment is a separate domain concept. So is the invocation of the clauses of that contract. Termination involves invoking a particular contract clause.

The job is to model a) the contract, b) the invocation of a specific clause, c) the record of that invocation (including its authorisations and so forth), and d) the consequential processing in other systems.

Whether you're working in FP, or Kay-style OOP, or Java-style OOP or, heck, SQL stored procedures, these are separate concerns, separate concepts, separate units of code, separate records, hopefully loosely coupled by whatever idiomatic form is at hand.

> Abstract concepts map very well to objects. The problem of mapping a business domain to computing structures isn't unique to OOP.

Yes, I agree in theory. What I meant to say is that many concepts don't map to physical objects.

> In this case, I'm slightly gobsmacked that no-one pointed out that an employee is not their job. The contract of employment is a separate domain concept.

This is what I mean by flamewars. Your solution sounds good, but there are other people on this same thread still arguing that it makes perfect sense for an employee to fire itself.

The litany of category and analytic blunders on display in that subthread is remarkable, but it’s hardly intrinsic to OOP, and mostly attributable to inexperience.
As with almost everything, when you try to stretch an analogy or use a single tool for every job, you are going to run into problems. Every tool has limitations and so does OOP.

Employee object is not a representation of the will of the employee. It is an interface with operations that are best naturally acting on an Employee object -- changing its state.

I suppose a manager is also an employee, but Manager inherits from Employee with a .fire() method seems wrong, no? I'd expect a method which takes another object or ID, not to change the state of the object with the fire() method.

Anyway, this is why I stay far, far away from OOP when I can - vast amounts of time spent on these questions which generate zero insight (at least the mathematical obsession with some parts of FP can be fun)

I agree with nimblegorilla: an employee that fires himself does not make sense. HR fires them, or some other entity, whichever has the power and permission, does so.

In the end, no matter what example you will pick it will always have the same kind of problem, that a higher entity is needed. And in the cases where that is not true, no encapsulation is needed. For example, "employee.age()". You can just do "olderEmployee = copy(employee, age = employee.age + 1)" (pseudocode) from the outside by having the age exposed (which is not classical OOP at all).

But I'm curious if you can find a counter example. Usually it stops working when global constraints come in.

after doing some smalltalk i think it would be Employee doesn't fire himself, you send him the fire message Employee fire: "immediately" so I kind of see where he's coming from here...
Yeah but that suffers from the problems already mentioned - an employee doesn't fire themselves.
Getters were popular with automatic tooling. They were intended for only UI models. In Java this was called the Bean Standard. https://en.m.wikibooks.org/wiki/Java_Programming/JavaBeans
Don't get me started on all the helpful tools that make it easy to automate antipatterns.

Heard fields should be kept private? Don't know why? Lombok to the rescue, let's generate all those suckers up when we could just as well made fields public.

The rationale is the same as that for an abstract data type. You don't need to know how a class structures its properties and directly coupling to a representation can burden future maintenance. This very rarely comes up, but when it does it is nice to change a getter instead of tracking down all the locations where it was directly coupled to the representation. It isn't at all obvious when you are writing a class whether future maintenance will need to change the representation or not.

This isn't as much a big deal in python, for example, where you can @property your getter and setter. In that way you can easily migrate existing code from direct data structure coupling to indirect/computed access.

.. except when manipulating proxy-ed objects, like Spring beans or ORM detached entities. The object you're calling the setter on is not the object (struct) holding the field..

I know, yet another idea thrown into the concepts cocktail.. (not saying these are correct justifications for the getter/setter pollution, just reasons why you find them everywhere in the wild)

It is better not to put business logic into data model - in order to:

1) Keep your business logic simpler.

2) Keep your data model simpler.

If you put both business logic and data model in the same class, then:

- When you investigate data references pointing to your class -- you will see references noise from your business logic.

- When you investigate business logic references pointing to your class -- you will see references noise from your data model.

3) Combined "business logic + data model" class is much harder to refactor.

So, technically, you can combine business logic and data model in the same class.

But practically, such code combining will significantly complicate maintainability of your code.

There is no relation to the paper. Recommend that you first take a look at it.

This item has no comments currently.

Keyboard Shortcuts

Story Lists

j
Next story
k
Previous story
Shift+j
Last story
Shift+k
First story
o Enter
Go to story URL
c
Go to comments
u
Go to author

Navigation

Shift+t
Go to top stories
Shift+n
Go to new stories
Shift+b
Go to best stories
Shift+a
Go to Ask HN
Shift+s
Go to Show HN

Miscellaneous

?
Show this modal