I really wish reaction videos where the reactor added anything of value weren't the minority.
ปีที่แล้ว +280
I think there's a fundamental misunderstanding of OOP at play. I write OOP, but I also start with one file, and split things out as they get unwieldy. Speculative programming is a waste of time, regardless of paradigm, but for some reason it's very common, especially for Java developers. I never write abstractions before I need them, I just make a basic implementation, and when I realize an abstraction would make sense, I refactors.
@@MadocComadrin He's got the correct picture. OOP is well defined. It's just that "OOP" programmers are really multiparadigm programmers who don't follow OOP well at all. Most of the objections you're sure to have is that he's not going into imperative code to resolve these issues he mentions. You don't get to claim ground for OOP without adhering to it. You also gain no benefits from OOP if you don't especially for properties like encapsulation and separation of concerns. If your "OOP" is just data with member functions that could just be free functions you're not doing OOP at all. It's a common mistake that the boundary of imperative to object oriented is when you have member functions. But that alone is purely imperative.
The issue is, refactoring is a pain regardless of the paradigm. If you end up writing an entire class, you most likely will end up splitting it into different subclasses with an abstract class as a base, which is not... easy to do. I find it better to write abstractions when you understand the problem and know what to write by looking ahead (figuratively). For example: A base class ASerializer, and subclasses that inherit from that. Writing ASerializer first just "makes sense" because you ALREADY know you are going to write subclasses implementing this base class in the first place ie. "looking ahead". But imagine you did not "look ahead" first and wrote ASerializer logic on tons of different classes... now you realize you need ASerializer... good luck refactoring all that :)
@@MadocComadrin Every time someone criticizes OOP there is someone who will claim that "this is not what OOP is" or "this is not real OOP". Before OOP we had decades of design principles developed for procedural code, so what's the differentiating factor of OOP if not for the things described by Brian?
Exactly. Most of what Brian Will is against is speculative structure. OOP prescribes a lot of speculative structure. It's like having a startup with an open floor plan; and walls are only put up as necessary. This is different from having a bunch of barriers erected early on in the design; and having to punch through all the barriers as you figure out what you want. An Osterhout video about great programmers talks about shallow vs deep interfaces really gets at the heart of it. You want the smallest possible interface surface area (what the caller needs to know), that hides as much useful functionality inside as it can. You want DEEP interfaces into modules. And when you have these deep interfaces, it's ok for things to lack speculative structure internally.
About the architect bit. My greatest work experience yet was when I started a new project in the company I was working. When it started, the lead architect of the project had already planned out all of the major "modules" of the backend we were going to build. One month in, I already was needing to change parts of it because it simply did not work. In the end I basically only kept the module names, around 50% of what was planned had to be changed or entirely "re-architected". 👌
Disadvantages of Object-Oriented Programming 1.Requires more effort: It requires some amount of work to implement parallelizable programs. 2.Not straightforward to reason with: Bad design can lead to an unnecessary amount of indirections that result in unreadable esoteric code. 3.Larger Programs: Often results in larger programs than procedural code.
@@Microphunktv-jb3kj Object-Oriented Programming just some thoughts... 1.Does not require more effort that is a myth 2. It is straightforward to reason unless you're still a n00b. 3. If it results in larger programs then you're using it for the wrong problem, so think wise!
My programming philosophy is: Separate Code and Data. Separate Representation and Schema. Represent Data using Generic Data Structures, so that it can be easily operated on. Prefer immutable data and persistent data structures. Be Functional for Stateless Logic, and Use Objects sparingly and with a purpose. Focus on Readability, Performance comes later. I used to hate OOP, but I have learned that certain parts of it are indeed useful, others (inheritance) are not so much. My biggest concern with Regular OOP is that it mutates state in a random manner and it hides information, making data manipulation cumbersome and inefficient. If you can make state immutable and whenever you MUST mutate state, you localize it so you can keep testability, then your code quality will sky-rocket. If you store data in generic data structures rather than objects, then you have more freedom to manipulate the data and can treat it as a first-class citizen. These are just my thoughts on the matter.
I find inheritance useful, I think the problem is that I entered an industry that has OOP fatigue from decades of misuse. So I can understand why more seasoned devs hate it since it was used very frequently in unnecessary ways in the past.
Brian Will's videos are great. As someone whose used Go/Java/C#/Angular, I think C# does OOP right and avoids many of the problems often brought up against OOP. C# also mixes procedural, FP, and OOP together in a compelling package, that most often is just nice to work in. Ofc, there are standout issues like how null is handled, but overall its pretty good and does what I need it to. Yet, Rust is another beast all together and makes you question why there ever was a OOP vs FP contention, when it could have just been "the rust way" all along.
C# is really enjoyable to use and read when used properly, probably IMO one of the best syntax’s out there and it’s OOP, although you don’t have to use OOP if you don’t want
Hi, I develop in C# and DotNet since the beginning of it about 20 years ago, and I think it's still a very amazing, well-designed language, that has always evolved fast and extensively. But even with C# ideas changed a lot at Microsoft and they also had to admit that a lot of things could be done in a lot better and simpler ways, so they also went down the route of simplyfying things a lot when it comes to small applications. Just think about top level code, global usings, file scoped namespaces, and also the minimal web-APIs in AspNetCore. In the end, I see a lot of the same patterns of modern evolutions occur in many programming languages, at least in the ones that are popular. But what I also see, when it comes to older languages with really fundamental design problems like JavaScript, and in that way also TypeScript (because it's in fact just a thin wrapper around JS), those are hard to fix, and that also explains that issue in this video with the interfaces of TS metioned in the video here. I am a bit out of hope for that.
@@kidmosey Well, how exactly does the C# language really discourage you from doing that? I mean that's how I am interpret what you have written now. I think that you intended to say that certain guidelines you have been learning around C# programming do that, or that the DotNet libraries you are working with do this. But the language does not restrict you from doing that. If you take some older frameworks, like Windows Forms, or WPF, you are still getting deep inheritance trees. What I expressed in my previous reply is that C# gives you better options, but not that it holds you back from going the old ways.
I really like its rich syntax. Custom indexers & properties are great. Not to mention the reflection. If you want to make a moddable game, you would use reflection to load and inject DLLs. If you're forcing a game to be moddable, it becomes even more useful.
Splitting modules by concept is what I think he means by "for organization". He was answering the question "when do we start splitting modules" and not "how do we split modules".
Thing is "splitting by concept" is the most difficult part of programming. Meanwhile splitting code into "functions and state" is kinda easy thing to do but doesn't necessarily deliver meaningful value - by removing state away you are not necessary improving design/responsabilities of the function. But good code is all about good responsabilities, effective categorization and good abstractions. Sometimes abstraction can be improved by removing state from object and sometimes abstraction can be improved by making object stateful.
Module splitting by "concept"? What does that even mean? An entire "app" is a "concept." This suggestion doesn't get us anywhere closer to an answer about when to split things up. The reality is that there are many reasons to split code up, but if we MUST boil it down to a single rule, it's that we split it up in whatever way improves maintainability.
Codebase structure actually does mirror the team structure of the organisation. Splitting modules based on teams may sounds weird, but it ends up happening even if you don't explicitly do it.
The simple answer is Rich Hickey answer, OO is bad because it ties state + behaviour + time in a single construct. Those are 3 separated concepts and should be treated separately. In my experience, OO leads to much more boiler place than FP. FP has its caveats if the language it's attempting some kind of purity. But most of the time it's just data (map, sets, list, vectors) and functions that produce new values, abstractions like objects get in the way, because you need to create more things (functions, classes, wrapper) to simple work with an existing object. While if you have your data separated from the behaviour you can reuse both the data and the behaviour. You can still make stuff that sucks, but at least has to return some datatype that you can work with however you want, instead of an object, that has it's methods saying how it likes to handled.
That may be true, but in a multi paradigm language like C++ the idea of objects or OOP there isn't so much the abstraction that "everything has to be an object", but more on lines with the robustness of the language that allows the programmer to create an object that becomes or acts like a new data type where the compiler will treat them like a built in type. You can create a class or a struct with members - fields that could either have public or private access and yet when another uses that object as a type, you can use it with existing operators such as any of the arithmetic, comparison, or even bitshift operators like streaming the contents of a class into a stringstream object that can later be piped to either the console, to a file, or some other output device. Not all OOP is bad, but not all of it is good either. Some languages where "everything has to be a class object" is a different story. Sometimes having just basic primitives that can be acted on is just good enough.
@@skilz8098 Your making the classic mistake of confusing OO with defining new datatypes by composing other datatypes. "Not OO" doesn't mean you exclusively work with primitives. In fact, in "Not OO" you tend to define alot more datatypes than in OO the OO world. C has structs, Fortran has structs too. Most functional languages don't just have structs / records, but extremely rich type systems that allow for much more expressive data modeling than OO languages. What separates OO from other types of programming are class hiearchies via inheritance, data encapsulation and coupling datatypes with behaviour.
You have apparently only worked in an environment where OO was done wrong. Read Design Patterns, the Model-View-Controller pattern for UI work separates all 3 of state, behavior, and time up into 3 different objects that are intermingled. The strategy pattern implicitly states that the object that holds the strategy object contains the state and the strategy object determines behavior. When your code has well designed OO principles put in as part of the coding standards, and he people adding to the code understand those patterns, and when to and not to use them, OO is far better than functional or procedural programming for anything with more than 5,000 lines of code. To do this requires regular code reviews, not for effectiveness of the code, but to make sure it follows the coding standards. Once you step out of a pure OO approach in the code base, the code base becomes a monster to maintain, but while you are writing pure OO it is far better than any other style of programming. To get the skills to program pure OO takes a couple of years to develop after college with pair programming with a senior developer, and no company is going to want to pay for that with the turnover in industry these days.
the only thing i hate about oop is the inheritance, when it comes to do unit test is so painful to do it. So the alternative to inheritance is the composition that i like btw.
It sounded to me like “OOP conflates data types and modules” is talking about Java’s only module type being a Class, and the awkward class-with-private-do-nothing-constructor-and-static-methods thing you have to do for free functions. That could also just be the constant pain I feel from working with Java in my day job, and only Java 8 at that.
Um, packages would like a word with you! There's even package private methods, which mean classes in the same package can call the method, but classes outside of the package can't.
@@bobbycrosby9765 What does package-private have to do with not being able to define functions without first finding/coming up with some class to put them in? In C++ for example, you can start a file by just declaring and defining some functions, without needing to think about namespaces, classes, or types. Then, when it makes sense, you can define some type(s) with `struct`, wrap it all up in one or more `namespace`s (which may be considered a "[sub]module" or "[sub]context"), and converting `struct`s to `class`es as they grow if you want to split out interfaces or bundle data/state and functions as objects. In Java (as far as I know), you're forced to do all of that from the very start rather than introducing/letting use of those tools emerge when it makes sense to do so; hence "the awkward class-with-private-do-nothing-constructor-and-static-methods thing you have to do for free functions". If you're writing small, single-threaded command line programs, it might seem like a small difference, but my brain personally works a lot better when I can gradually ramp up to classes, and having that control makes a huge difference when concurrency comes into the mix.
@@ChaoticTrack They said classes were their only modules. That is flatly false, you can look at packages as modules, and you have things like package private to facilitate keeping stuff private to your module (the package) so no code outside the module (aka other packages) can see it.
I'm pretty much a lua developer (technically, what I write is a modern dialect of lisp called fennel, it's fairly similar to clojure but with lua rather than java) In lua, you don't actually have OOP, but you can replicate many of the same features and functionality As someone who started out learning java, it's a bit of an experience, but it's really nice and quite enjoyable
Tables are basically prototype programming like Javascript, don't lie to yourself, working in lua ends up you making oop without having oop... Which is fine thou, la is hated by so many people, but it's pretty decent. It's just evolving slower compared to other languages, but if r compare lua 5 and ES5 (released around the same time) lua is way better. Now the problem with lua is how it's stuck I'n that past (well lua evolved, but not much) while ES started to do giant jumps between versions. And I'm the case of lua, a lot of people will forever be stuck in lua 5.1 because of luaGIT. Which causes more fragmentation between lua users, luarocks and it kinda handicaps the future of lua.
@@rex_melynas Practically speaking, LuaJIT isn't stuck at Lua 5.1 but instead branched off from Lua 5.1. The real hit to it in terms of usability is the author rejecting the concept of releases, which is basically just LuaJIT grabbing one of JS's problems (feature uncertainty) for no reason at all. Rolling releases are fine when it's just technical details changing, but there're some pretty big changes all under the same version name...
Julia is designed with separation of modules and data types and its very good. It uses multiple dispatch to get the functionality of class methods, but without conflating modules and data types. it just feels good.
Julia is a good language. Wish it can replace Go, but it's ecosystem is so far removed from the greater software engineering ecosystem that I don't know where the language goes...
Everytime someone talks about OO I would like them to clarify upfront: which style of OO? cuz the value passing message-passing style of Smalltalk and Erlang are pretty functional and nice
I was searching for this comment, In my company we use Smalltalk for everything and not even the decision behind the language are very nice but also the common IDE that the smalltalk dialects have are so good. It's very easy to navigate between messages and classes, the only disadvantage is the version control system, Pharo has the possibility of using Git, but the common is to use ENVY.
You mentioned it on some points. But it feels like there is a miss on simple concepts like DRY, KISS, YAGNI and especially SOLID which makes any programming style easier to execute. I saw terrible OO code but refactoring the whole project taught me alot. The result was a clean, well structured Java project with akka, sql etc. Less coupled layers between database ORM, the services, actors and the endpoints. And one of the most awful things I saw there was a huge monstrosity of inheritance and tests. Tests inheriting from other tests. omg. I purged it completely and made proper unit and/or integration tests for the same use cases. It's always about the real problem that requires a proper solution. pragmatism is key. OOP or not, people can f*up anything if they do not know how to use tools. It is not the tools fault. At least not always.
OO is programming with objects that communicate via messages. Pretty much nobody actually does that in practice, people call anything OO as long as it has encapsulation and dynamic polymorphism, neither of which are unique to OO. Static polymorphism isn't really an OO concept at all, but is IMHO preferable to dynamic polymorphism when applicable; the only reason we don't use it more often is that most programming languages just straight up don't have a way to even express static polymorphism, or at least not in a way that isn't extremely cumbersome to use. And of course, most things don't need to be polymorphic at all.
You need an OO language to properly translate a domain driven model into code. I have been a developer for more than 10 years by now, and when you can get rid of your perceptions you start seeing different programing paradigms, technologies, frameworks, languages as tools. Choose the right tool for the task.
Architects are perfect for big rusty corporations who like to set 10 meetings to triage the epics to handle a change request for a json field that needs to be passed down 3 microservices with an ETA of 4 sprints
The more data you're working with the more OO matters. Overengineering your classes because "patterns" is just layers and layers of crap. I call anything that uses classes OO. Even if it is mostly just organizing data structures and not complex relationships between objects.
I have had the wonderful opportunity to pick tools and replace a number of redundant systems with one good one. I picked Nim for C compatibility, native speed, and its ridiculous modeling power. Coming from the functional world, I love its defaults: Parameters are immutable unless `var`, and procs have their own context. This makes the functional/procedural style of building modules of related functions, passing data to each other to produce new outputs, or more often down a pyramid of calls that construct a more complete bundle of functionality, crystal clear. It's beautiful. I have one module that does C++/Java-style OO and it fits the purpose beautifully, but NOTHING outside that module is modeled that way. My only headaches in Nim have not been modeling problems (with enough thought it deftly guides you to the correct answer), they've all been dealing with decades-old anachronisms of the C toolchain, which Nim uses. I love the speed and compatibility though. Linking static binaries with musl-libc feels *very* good.
People just need an enemy to justify their bad decisions, laziness and sometimes their jobs, right now is OOP, classes are now considered harmful and perfectly working code should be discarded and rewritten to be more pure. I prefer to mantain software made with OOP than the unholy mess that is haskell, untyped spagetti javascript and non performant python code.
Unfair generalisation. I don't like Haskell syntax, yes I agree it's bad but I like the concepts it comes with, programming in haskell just hit different to programming in Java or PHP. Think of it like music different people different tastes but unlike music programming paradigms feel more like religion. Also it's hard to write good functional code I've seen a lot of functional that's just procedural code.
@@ea_naseer when you have a 10 years old codebase in java, or C++ that works, and suddenly a religious zealot wants to throw the code because OOP BAD i take offense and defend the old and ugly code over the more pure and ideal future, in you analogy, im more like an atheist, what works works, most languages now are multiparadigm anyway.
22:10 Extending EventEmitter works well but I prefer using Signals (an event dispatch/registration model pioneered by Robert Penner - same guy who gave us easing equations in Flash/ActionScript back in the day). There is an NPM package called mini-signals that's a fairly good implementation of the Signals pattern. The Signals pattern embraces the old adage 'Favor Composition Over Inheritance". Instead of extending EventEmitter, you create a dedicated Signals object that has dispatch and addLIstener methods. Decoupling event dispatch and listener registration from its client allows multiple clients to independently leverage the same signal. Signals are pretty flexible and allow for all sorts of messaging patterns (message buses are amongst my favorite).
The only time I found inheritance really usefull was when I actually wanted to override a class behaviour. I worked a lot with the Laravel framework and when I needed some internal class to work differently, from the framework or some external library, I could easily extend the class, override the methods I cared about and then tell the dependency injection to use my class over the default one, or even, when to use my class over the default one, pretty neat.
This is the perfect scenario for Inheritance - This is how we guide the users of our SDK/framework (both internal and external) - 'If you need to change behaviour of Class XYZ then you can override methods ABC, DEF.' Inheritance needs to be used judiciously, but in the right place it is an extremely powerful tool.
Used inheritance when creating a flat matrix class in typescript; had a Readonly version as the base and a regular, mutable version that inherits from Readonly but has a .set function. Cases like these are the only times i use inheritance, but they are very powerful.
Yes, this is one of the two good uses of inheritance. The other one is creating base classes for one kind of classes, for example models or controllers. Then you can make the classes much smaller and focus on the important logic.
Inheritance is fine, the problem is it is extremely easy to exploit and most devs are dumb and will exploit it unless properly taught how to use it or kept in check by their seniors.
OOP is good, but with an A$$ The Rick. No honestly, it's good in some domains like line of business stuff, but only if you heavily use FP concepts also. OOP without FP to me is like the old OLD way to do OOP, with tight coupling everywhere, unexpected error states easily introduced when something is (so easily) not used as it was supposed to be used, super deep levels of abstractions that are most of the times not even needed, etc. But OOP has gotten better in recent times, especially with modern versions of their languages and accompanying frameworks and architectures... it has to.
OOP is kind of all about lose coupling - all early OOP languages had came with tools that allow achieving lose coupling pretty well. The "old OOP" style code that everyone hates in fact often is not OOP but procedural style code with extra syntax sugar of classes and inheritance - basically people who used to know how to write procedural code - learned about oop from idiotic books "head first OOP" or similar that the only thing it teaches is how to organize your pets - cats and dogs - under common base class 'animal'. Little who took time to read into more serious literature on OOP.
This is reminding me to read On the Criteria to be Used in Decomposing Systems into Modules, a 1972 paper by D.L. Parnas, in which he argues that one very effective criteria is localizing change. I think this is where you get things like the Open Closed Principle. OOP is optimized for being able to modify the behavior of a system with minimal changes to existing code, if you do it right. But most people write code that is constantly changed or refactored instead of adding classes that override existing behavior, so in those cases OOP provides little if any benefit.
Really depends what you mean by OO. For re-usable data structures mixed into procedural code, OO is great. No issues. For de-coupling complex systems a la Spring, it's quite a major different idea of OO.
This. I use classes for low level data structures and APIs, but anything to do with logic, orchestration, high level abstraction is all composable functions.
If you reviewed the Mario Kart 64 decomp source code when it gets completed I think you would find it's lack of good programming hilarious. No real memory management (actual memory management funcs unused), manually written DMA calls everytime instead of a manager. Copypasta actor behaviour code. Inconsistent use of array + offset and array[offset]. Code in random places that do not belong. Overly long functions, fall-through switch cases, unused variables, etc.
I like having methods on objects. I don't like len( arr ). I do like arr.length. I like "I'm a string and I know how to do stuff".contains("stuff"), button.on('click', (evt) => doAction() ), list.find( (x) => x.hereIAm ). My IDE helps me remember the options I have when I'm given a specific type (dynamic languages mess this up, but it's ok). This is OO and it works really well, and it's powerful. If you decide EVERYTHING MUST BE PROCEDURAL!!!! You have to give up all of that, and I don't want to code in XLib or Windows C Lib style code ever ever ever again. Anyone who is convinced procedural is better doesn't remember the days before OO, and what a nightmare those were.
I've always had an issue with architects who don't do the things they architect. And this doesnt apply only to development. It's the same when we do IaC.
Programmers have always been very dogmatic about ways of doing things. Its easy to see that our egos are waaaay tf out of control. The first time I heard his rant about "OO bad!" I picked up on his bias immediately. You can go watch his original video and pay attention to when he says things like "why do this, when you can just do this?". Thats just bias, a preferred way of doing things. Because ANYONE can just as easily assert the same thing in the opposite direction. And I guess having a youtube channel makes people feel self-important, to compound the average developer's out of control ego
The good parts of inheritance can be had with Haskell type classes. It’s not a set-subset relationship. It’s more like “this is an instance of this principle, here is how”, with the “how” filling in any bridging functions not defined by the type class. So you can implement many different type classes, in a way more flexible than that allowed by the set-subset relationship of inheritance. Functional master race
5 หลายเดือนก่อน
At the end you answered someone's question about coding, seniority and architects. You've mentioned that your team was fully seniors and I have similar experience on that situation; everyone was able to take responsibility of the architecture together and everyone produced code for it. Separate architects and/or less-coding seniors might be more common on teams with significant portion of junior developers. In that scenario someone more experienced needs to take care of the big picture and also spend more time directing and helping the juniors.
I've done so much OO since I started programming I really struggle to break away from it, most problems seem so easy to handle in OO for me. I would love to learn more functional programming and procedural but it's so frustrating to not be able to come up with the solution quickly and to relearn everything. I guess it just made me impatient
If done correctly OOP has its uses and can be quite a powerful tool in the tool box, however it is not the answer to everything. I think it honestly depends on the task at hand and what is required of it. I'm not a "big promoter" of it nor am I "against" it. I think that to know what OOP is and how to properly implement it and to properly use it is more important than the debate for or against it.
On the game I'm making at home, the behavior class was divided into two files. The class itself is the state module, and the other file is strictly for organizing specific behaviors as extension functions of the individual behaviors, that can then be swapped in as delegates. While not identical to how he suggests, it does seem to be an implementation of separating the logic from the state. (Working in Unity, I can't help but be in object hell if I don't do it right.)
But at this point wouldn't you be better off just writing procedural code, because what you are describing doesn't seem much different than what I write in C. I have a struct with data, and functions that operates ton that data both are separated.
@@pierreollivier1 my reason for dividing them was mostly for organizational purposes. That being said, I have completely reworked my behavior class to work more like a finite state machine for ease of use, as of a few weeks ago.
@@pirateskeleton7828 I love finite state machine, I use it a lot for prototyping actually, It makes the incremental process of discovery so easy because you don't need to build a lot of infrastructures for it to work, at first your FSM can work on one case, than as you extend it more enums and functions pointers you get closer and closer to the end goal. On top of that the debugging of FSM is amazingly trivial.
The difference between procedural and functional programming is that functional programming has a big emphasis on pure functions as much as possible, which means you don't have any guarantees on the order in which things are evaluated, for example, in opposition to the imperative declarations you would see in a procedural software. So basically, programming with functions != functional programming
I'm sorry you had to deal with bad architects. I do know some architects that should of been moved to a management position. I understand the frustration. For reference good architects at our firm are : leaders bridging the product owner and SM/PM with the team; designers of solutions; ( Meaning we make the draft and evolve it with our team to something production worthy and keep track of changes to architecture. ) participate in the writing and validation of code. We also write the high level and detailed architectural documents as a reference for other teams and update them. Finally, architects almost never work alone. They hold architectural boards with other architects ( data, security, integration, infra, etc. ), explain findings and get them approved by other architects. Maybe you can have the software engineers do all that. But in my experience, lots of stuff get pushed to the side in favor of just implementing. That can lead to spaghetti code, architecture and technical debt.
Every time I see these OOP critiques, I'm reminded of Borachio's retort to Conrade: "Seest thou not, I say, what a deformed thief this fashion is? how giddily a’ turns about all the hot bloods between fourteen and five-and-thirty?"
I develop a lot of C++ code in and around AUTOSAR. First, we have a lot of hardware-specific registers. With inheritance, you can create a hierarchy of datatypes you can check against in tests or runtime. Also, we have a lot of virtual function classes. With the proper settings those force you to implement low-level functions for any "is-a" relationship, that will use a specific interface from application to hardware. It`s generally not the worst idea to separate a technical system from its physical models and describe each of them in a class.
There is this really interesting talk by Evan Czaplicki, “The Life of a File” where he talks, among other things, how the acceptable size of your file is dependent on things like the language you are using. That’s be an interesting talk to watch here.
If your Utils package has 900 classes, it is time to split that package up. Single Responsibility Principle shouldn't just apply to classes/modules. It should also apply to packages. And if you end up with too many packages, you should organize them into hierarchies of packages that make sense. It is Single Responsibility Principle all the way down.
This is a classic academic vs practical conflic. A module is an abstract academic concept as the guy discusses it. It could be one function inside a larger file, it could be a folder with many files. All it means is some set of conceptually grouped together code. So like that example of the one function that doesn't fit the concept, that just means that one function is its own module.
OO is great. Just being able to separate implementation details from APIs easily is amazing enough. Having custom datatypes, great! Inheritance? Not quite so great, but it makes sense often enough. It's a tool to be used when required, not a mandate to use it. The same for the whole OO concept. You wanna have a game and represent a tank? Just make a tank object. You want your tank to have different weapons? Aggregate. I don't see a problem with that at all. I wanna see the the guy trying to use a functional language like haskell for that. That's just insane. Hiding your datamembers behind a well defined, public API just makes sense. I don't understand all those c people shoving everything into a global namespace and then having to come up with ridiculous function names like scanf, sscanf, sscanf_s and whatever other permutation of leading s, trailing _s and middle n there are.
Neither implementation hiding or data types is a concept introduced by OOP. No one seems to agree what OOP is, but most definitions uses the "four pillars of OOP" as a base point. In the "four pillars of OOP", inheritance and polymorphism are the only really unique features of OOP. The remaining pillars, encapsulation and abstraction, are not unique to OOP what so ever. Even C can make implementation details private using the static keyword. Using your tank example, the obvious approach would be to use some sort of structured data, e.g. a struct in C. This has existed decades before OOP was invented. What OOP brings to the table would be to introduce a parent class GameObject and a subclass for each tank. Then using polymorphism to "abstract" parts of the codebase, only to struggle 1 year later when you want to implement that North Korean tank that doesn't neatly fit the existing abstractions of tanks. OOP proponents like you are essentially saying "OOP is good if you don't use the defining features of OOP"
@@tapwater424 sure, you can make your tank struct in c, but you can't put the fire method or the move method into it, and instead you come up with ridiculous names and insane parameter passing. Anyone can just do whatever to your tank objects and you have to do some debugging to find out why all your tanks suddenly have the same fuel value because the bug can literally be anywhere. How are you going to give your tanks sabot shells and heat shells at the same time into the same ammo rack? What if you want to add hesh shells later? With a complicated mess of logic, that's how. Nevermind all the other advantages of c++ over c like templates or the standard library. OOP gives you options. I don't care what OOP is or what you call it or what people agree on what it is, I care about what I can do with the language. And if I can allocate resources that deallocate themselves automatically with no or minimal overhead, then that's something that c can't do. Just because a shovel has a sharp edge doesn't mean that you go into the woods with it to chop trees. c++ is 99% compatible with c. I don't see why you wouldn't use it over c, even if you don't use classes, it just gives you more options. classes are just yet another tool to use if your problem calls for it. Also if that north korean tank doesn't fit the definition of a tank, then it's not a tank. And I know there are some whacky tank designs, but a tank generally is a mobile, armored box with a gun. You either introduce an intermediate class, like standard tank or your derive your north korean special tank from vehicle instead because it's not a tank. btw, north korea just uses old russian and chinese crap. the kind of junk russia is pulling out of it's storage right now. Stuff that is very much what you think a tank is. There are much more stranger designs.
@@FalcoGer This is not a C++ vs C debate. I just used C as an example because it is a language invented before OOP became a thing. The fact that C++ has useful features is irrelevant. And your perception of C has no basis in reality. Tons of useful software has been written in C. It has been used for video games, operating systems, version control. Really any software you can imagine.
@@tapwater424FalcoGer is completely right and your tank example was exactly one that explodes in your face later in development if written in a non-OOP language like C. The fact that there is software like this exists written in C, doesn't mean it's anywhere close to being as maintainable as similar software written in an OOP language like C++. If the Tank interface in your example doesn't satisfy what you need for the North Korean tank, guess what, you can and should refactor it since there is an obvious design flaw then. The point is that you can actually refactor it locally and in every class that implements the Tank interface with your IDE and Compiler giving you errors when you missed something instead of having to look around in 10k loc to check where your Tank struct was passed around as void* and dereferenced in places you didn't even know existed before touching this.
@@tapwater424I disagree that inheritance and polymorphism are the two core pillars of oop, encapsulation and polymorphism is. The fundamental concepts of oop is that by associating behaviour with data (i.e. encapsulation) doing the same thing to an object will produce different behaviour dependant on the data (i.e. polymorphism). You can have good OO using only interfaces and no inheritance.
Appreciate the long form videos man. I'm just learning to code, for personal interest mainly but potentially to assist in a future career change. Your content is helping mold and shape as well as contextualize what I'm learning in the courses I'm taking online. Keep up the good work!
Global state mutation is just unmaintainable in any non trivial codebase, so no, functional is not the same as procedural.(Tbh, I'm not against a "global" state in a module/dll if it offers a reasonable performace gain in a hot path, but that's just a possible optimization if it doesn't affect the whole program)
23:02 Atleast in Rust, if the entire benefit you want is typing two words to get a benefit instead of typing a lot of words, a blanket implementation using #[derive(EventEmitter)] wouldn't be too bad.
i am curious how this would work because you need to add properties to the struct (something to track the callbacks) and i assume you don't want them ackshually showing up in the LSP (unless you do)
@@ThePrimeTimeagen I can't say I'm knowledgeable about this, but can't this be done by just having a private EventEmitter data member (which obviously itself has public fields for manipulation inside impl blocks)? This way the LSP would know not to display its details and methods. But still, there are problems with this. 1) Actually modifying the struct. Derive macros don't allow changing the struct. For that, you require attribute macros. Syntactically they're similar, but feel "special"/"magic" to me for some reason. 2) This will change the repr of the struct and if something somewhere is relying on it, it will break. 3) Debug printing will get ugly, since now an object irrelevant to your main functioning is filling up your view space. This part actually bothers me a lot more.
Oh, well: OOP. What are the three major goals of any larger SW project / codebase? Correctness, performance and testability. There are other aspects of course like reliability and maintainability. One of the major strategies for correctness is keeping errors "local", not spreading out into 100 files or modules. That's why we have modular programming: first functions, then compound types, then classes / interfaces with data hiding, then patterns like dependency injection with lose coupling of components. This helps also with maintainability and downwards compatibility, so that new code can work with old code. But OOP is only one paradigm of many in programming. When my 20+ years in the industry taught me one thing is, that OOP isn't per se good or bad, success in a project depends on many factors of which choice of programming language is only one of them.
I prefer PHP traits where CanQuack is a chunk of code that is essentially injected into the class scope, and interfaces like Quackable are contracts for polymorphism. When you combine traits and interfaces you can easily satisfy the contract without needing to create thousands of lines of boilerplate like Java. The catch though is traits in PHP are annoying to override because you have to essentially rename but still include the default behavior to make room for the new definition. Is this something that Rust solves?
"Like Java", what decade are you in? Java might've been verbose before Java8 ten years ago, not anymore. Newer versions of the language allows you write as compact code as any other language.
Good OO is very close to FP in many aspects. Well constructed objects are nothing but a set of partially applied functions. Typical OO in the wild tend to be more class oriented programming than OO. And isn't a Monad a special kind of object implementing the monad interface?
Engineers have at least a high level understanding of architecture in the same way that architects have a high level understanding of engineering - in the same way as physical building - the point of an architect should be to help glue together the developers ideas to solve a problem and be flexible, like a servant leader - not act like a dictator - especially in anything that isn't pure Waterfall - and by the time it matters in Waterfall the architect has left the building anyway.
14:45 utils is a code smell in real world applications too. Organize code by type. Even if it's just string. Not like it changes much, or you need to look at all number utils and string utils at the same time.
I prefer to organize by feature at the top level and by type beneath that. I've found the inverse hard to navigate, even when e.g., using a framework where every feature will have stuff like 'ui_components', 'controllers', 'models', etc. (In that case, I start with a flat structure within the feature context/module and only start to make folders for the types if it grows to ~8+ files or if I want to hide files that I don't really care about, esp. in Java where it's one class per file) Further, I _do_ actually _want_ to see all the string utils at the same time if I want to do some generic operation on e.g., strings in my 'accounts' context when the standard library doesn't provide it. I don't want to dig through every file in my codebase (or do that in my head) to see if I've already written that logic. If you're using Vim or an IDE, you can fold all the function definitions or open the Structure menu to only see the signatures.
> “Utils is a code smell.” The problem is that that statement is a statement that exists outside of practical work. YES. Truer words have never been spoken.
if you like oop so much then why dont you override the public private polymorphic abstract pure virtual immutable protected interface class instance container manager and marry it?
Casey Muratori has some arguments against private fields, namely that getters and setters should only be used if they perform some kind of processing on the underlying data and that public fields allow you to be more flexible with your testing down the line. I mostly agree but still use private fields as a way to add discipline to my implementation basically forcing myself to design a good interface rather than accessing things whenever and however I like which can eventually lead to spaghettification if I'm not careful.
singleton -> static class inheritance is for stable libraries and as an easy way to have interfaces: instead of writing the parent class first, you write the parent class as an interface (pure virtual class) which takes like two seconds since you just forward declare virtual functions and common data members but don't define logic.
The poll results are not that weird. In most universities OO gets taught as the end-game content, so you leave it looking for an OO language job. And some people never leave that dark tunnel xD
@25:13 no, it is actually obvious when an abstraction is useful because it reduces the code complexity or the amount of code written substantially. Try writing something equivalent to a custom C++ container without a class and then make a class. The difference is massive--there are useful abstractions and when things get too messy they become an obvious solution.
functional code isn't just procedural code with functions. You could potentially say it's procedural code with *only* functions or better yet procedural code with *only* *pure* functions but it isn't just procedural code with functions. It has a lot in common with procedural code but procedural code is using structure to manage code OO is about message passing and I would argue has become about controlling access to shared state and functional programming is about the complete abolishment of shared state.
29:15 Architect = dictatorship people collaborating together being architects of their own segment of the project = democracy Memes aside, the entire process was described is like how legislation is supposed to work. Someone comes up with a new law or an edit, it gets brought to everyone, they voice their opinion, make edits and loop until all are happy. It's a very good analogy, imo
modules here is more like a folder, a group of code the fit together, so you don't get the user/role objects that need code from each other thing, but user and roles is in the same module but product/catgories may not be in the user/role module as they don't fit together.
I really don't get that part on pure functions writing in files. I consider pure functions those which have no side effects, writing files do have side effects. What got me was OOP "promotes small abstractions". I remember Pesce writing something like juniors understand better (and tend to build) simple structures with complex logic, seniors prefer complex structures with simple logic. Maybe the latter requires higher skills than the former but I wouldn't consider it wrong just because it's not something anyone can achieve. The former, in this pov, looks more like a white flag.
"..in almost all cases our state modules are singletons" - Prime starts shaking head wildly :'D I would argue modern OO with interfaces is all-in-all a good thing and for more junior devs it helps to learn the right way to reason about code. Rust's type system and impl-s are far more natural to me now, but 3 years ago I was not a mature developer enough to understand it in depth.
The problem with a "utils" folder is the generic name as it doesn't convey the content. Could have been named "stuff". The promise explode function named, while being a utility function, does have a very specific function and should therefor be in a folder denoting its usage, such as "promises/interop".
Not sure why this entire generation of coders hate OOP. I love OOP because it increases project organization, abstraction, and meaningful code. Things may be turning more to composables, but still OOP has its place. Dealing with very large projects I will always choose OOP.
Agreed. It's not always good, but it often is. The big problem with OOP is that so many people are dogmatic about it and think it's always the right approach.
I like to mix the two together with classes, properties, interfaces and objects from OOP and the methods itself written in a more functional style using C# linq/java streams. OOP makes organization super easy
Yeah OOP has a lot of good principles to it that will really help your code in general once you work on anything of meaningful size. Honestly the guys that hate OOP I really have to wonder if they do nothing but small programs. I can't imagine any kind of even moderate-sized application being done in all imperative style without having a code base that's a huge mess. It also works very well when you factor in any kind of IDE-like behavior in your editor, because you can take any object and see the auto-complete for the methods of that object. So whether you're dealing with a string, a vector or some custom object in any codebase you can always get an idea of what things that object can do by its methods. And to be honest, all sufficiently complex programming starts to look more OOP. Even C's standard library with fopen, fclose, fprintf, etc are all essentially primitive OOP just without the handy OOP notation and associations. You have this object that's holding a state, you largely keep the implementation details unimportant and just hand the user functions that do operations with that object and translate it into basic types without ever having them need to know what's going on behind the scenes. It's just a lot handier if you have a real File class, so you know instantly where to check it for methods. Not to mention all the other features like RAII you can then build into it to not have to worry quite as much about cleaning it up.
@@taragnor Yup! Exactly. OOP isn't the end all be all, but in general OOP is important when it comes to implementing specific design patterns like MVC, dependency injection, etc. Especially useful when using external libraries/modules. Also makes documentation easier, and code reusability. There are just WAY too many benefits to it. Whats even funnier is that all these people who are against OOP don't even realize they are benefitting from its practice. Like all these newer generation React devs... Like you guys realize that components are still just objects right?
I guess by collaboration in teams they mean teams with a large scope. Say with a OS, teams working on user level features will be dependant on a clean public interface from the shell team.
I don't like coding in OO *or* Java, but i like the house it bought me :shrug:
this is the way
Lmfao Amen
real
the house holding the garden, the garden holding the city, country, continent, world. universum.
I commend you for your mental fortitude. Not being ironic - that takes extreme discipline.
I really don't like logic based programming. I'm an emotional coder.
Personally... rage coder, just scream at it until it works.
Underrated if you ask me.
I'm a big proponent of POOP: Procedural Object-Oriented Programming.
php by accident is very good at POOP being procedural by default with Java style OOP as an add on
Yes, we also do lots of poop-oriented programming in our teams.
people order our patties
You're suggesting a lavender marriage. Just let OOP go and move on with your life.
You know it’s gonna be good when the video is 3x longer than the video he is reacting to
did you do a minecraft thing?
@@ttrss Bit late, but I've had some fun in Minecraft yeah 😉
especially when he speeds it up halfway through
asmon does that too, but he only adds general nonsense and doesn't add anything to the video
I really wish reaction videos where the reactor added anything of value weren't the minority.
I think there's a fundamental misunderstanding of OOP at play. I write OOP, but I also start with one file, and split things out as they get unwieldy. Speculative programming is a waste of time, regardless of paradigm, but for some reason it's very common, especially for Java developers. I never write abstractions before I need them, I just make a basic implementation, and when I realize an abstraction would make sense, I refactors.
Having a fundamental misunderstanding of OOP is Brian Wills modus operandi.
@@MadocComadrin He's got the correct picture. OOP is well defined. It's just that "OOP" programmers are really multiparadigm programmers who don't follow OOP well at all. Most of the objections you're sure to have is that he's not going into imperative code to resolve these issues he mentions.
You don't get to claim ground for OOP without adhering to it. You also gain no benefits from OOP if you don't especially for properties like encapsulation and separation of concerns. If your "OOP" is just data with member functions that could just be free functions you're not doing OOP at all. It's a common mistake that the boundary of imperative to object oriented is when you have member functions. But that alone is purely imperative.
The issue is, refactoring is a pain regardless of the paradigm. If you end up writing an entire class, you most likely will end up splitting it into different subclasses with an abstract class as a base, which is not... easy to do. I find it better to write abstractions when you understand the problem and know what to write by looking ahead (figuratively). For example: A base class ASerializer, and subclasses that inherit from that. Writing ASerializer first just "makes sense" because you ALREADY know you are going to write subclasses implementing this base class in the first place ie. "looking ahead". But imagine you did not "look ahead" first and wrote ASerializer logic on tons of different classes... now you realize you need ASerializer... good luck refactoring all that :)
@@MadocComadrin Every time someone criticizes OOP there is someone who will claim that "this is not what OOP is" or "this is not real OOP". Before OOP we had decades of design principles developed for procedural code, so what's the differentiating factor of OOP if not for the things described by Brian?
Exactly. Most of what Brian Will is against is speculative structure. OOP prescribes a lot of speculative structure. It's like having a startup with an open floor plan; and walls are only put up as necessary. This is different from having a bunch of barriers erected early on in the design; and having to punch through all the barriers as you figure out what you want. An Osterhout video about great programmers talks about shallow vs deep interfaces really gets at the heart of it. You want the smallest possible interface surface area (what the caller needs to know), that hides as much useful functionality inside as it can.
You want DEEP interfaces into modules. And when you have these deep interfaces, it's ok for things to lack speculative structure internally.
OOP is like:
"Abstract spherical horse in a vacuum"
😂😂
Don’t conflate OOP with inheritance. Composition works in OOP as well.
@@jamesriordan5461 Does it make OOP less abstract?
About the architect bit.
My greatest work experience yet was when I started a new project in the company I was working.
When it started, the lead architect of the project had already planned out all of the major "modules" of the backend we were going to build.
One month in, I already was needing to change parts of it because it simply did not work. In the end I basically only kept the module names, around 50% of what was planned had to be changed or entirely "re-architected". 👌
yup, you can't plan everything, you do little bit by little bit and refactor a lot
Disadvantages of Object-Oriented Programming
1.Requires more effort: It requires some amount of work to implement parallelizable programs.
2.Not straightforward to reason with: Bad design can lead to an unnecessary amount of indirections that result in unreadable esoteric code.
3.Larger Programs: Often results in larger programs than procedural code.
@@Microphunktv-jb3kj Object-Oriented Programming just some thoughts...
1.Does not require more effort that is a myth
2. It is straightforward to reason unless you're still a n00b.
3. If it results in larger programs then you're using it for the wrong problem, so think wise!
Yup this is normal, even for small projects. This is why the concept of abstraction exists.
@@mvdrider ...These aren't exactly specific to OOP lol.
My programming philosophy is:
Separate Code and Data.
Separate Representation and Schema.
Represent Data using Generic Data Structures, so that it can be easily operated on.
Prefer immutable data and persistent data structures.
Be Functional for Stateless Logic, and Use Objects sparingly and with a purpose.
Focus on Readability, Performance comes later.
I used to hate OOP, but I have learned that certain parts of it are indeed useful, others (inheritance) are not so much.
My biggest concern with Regular OOP is that it mutates state in a random manner and it hides information, making data manipulation cumbersome and inefficient.
If you can make state immutable and whenever you MUST mutate state, you localize it so you can keep testability, then your code quality will sky-rocket.
If you store data in generic data structures rather than objects, then you have more freedom to manipulate the data and can treat it as a first-class citizen.
These are just my thoughts on the matter.
This was insightful. Thank you for the comment :)
I find inheritance useful, I think the problem is that I entered an industry that has OOP fatigue from decades of misuse. So I can understand why more seasoned devs hate it since it was used very frequently in unnecessary ways in the past.
Can you explain "Separate Representation and Schema." a bit more?
That went over my head.
@wurmfabrik Yea, that's the point. Keep it simple stupid.
Brian Will's videos are great. As someone whose used Go/Java/C#/Angular, I think C# does OOP right and avoids many of the problems often brought up against OOP. C# also mixes procedural, FP, and OOP together in a compelling package, that most often is just nice to work in. Ofc, there are standout issues like how null is handled, but overall its pretty good and does what I need it to. Yet, Rust is another beast all together and makes you question why there ever was a OOP vs FP contention, when it could have just been "the rust way" all along.
Why did you put JavaScript framework alongside programming languages
Bc most here are ppl who only know JS
Isn't (modern) Java basically the same as C# in terms of OOP and mixing procedural, FP and OOP?
C# is really enjoyable to use and read when used properly, probably IMO one of the best syntax’s out there and it’s OOP, although you don’t have to use OOP if you don’t want
Hi, I develop in C# and DotNet since the beginning of it about 20 years ago, and I think it's still a very amazing, well-designed language, that has always evolved fast and extensively. But even with C# ideas changed a lot at Microsoft and they also had to admit that a lot of things could be done in a lot better and simpler ways, so they also went down the route of simplyfying things a lot when it comes to small applications.
Just think about top level code, global usings, file scoped namespaces, and also the minimal web-APIs in AspNetCore.
In the end, I see a lot of the same patterns of modern evolutions occur in many programming languages, at least in the ones that are popular.
But what I also see, when it comes to older languages with really fundamental design problems like JavaScript, and in that way also TypeScript (because it's in fact just a thin wrapper around JS), those are hard to fix, and that also explains that issue in this video with the interfaces of TS metioned in the video here. I am a bit out of hope for that.
@@kidmosey Well, how exactly does the C# language really discourage you from doing that? I mean that's how I am interpret what you have written now. I think that you intended to say that certain guidelines you have been learning around C# programming do that, or that the DotNet libraries you are working with do this. But the language does not restrict you from doing that. If you take some older frameworks, like Windows Forms, or WPF, you are still getting deep inheritance trees.
What I expressed in my previous reply is that C# gives you better options, but not that it holds you back from going the old ways.
Nothing in the language restricts this at all I have no idea what he’s talking about
@@kidmosey so its like how Java does it?
I really like its rich syntax. Custom indexers & properties are great. Not to mention the reflection. If you want to make a moddable game, you would use reflection to load and inject DLLs. If you're forcing a game to be moddable, it becomes even more useful.
Brian Will is the one who taught me C properly. not just "put this here here and here and now it says hello world congrats"
Splitting modules by concept is what I think he means by "for organization". He was answering the question "when do we start splitting modules" and not "how do we split modules".
Thing is "splitting by concept" is the most difficult part of programming. Meanwhile splitting code into "functions and state" is kinda easy thing to do but doesn't necessarily deliver meaningful value - by removing state away you are not necessary improving design/responsabilities of the function. But good code is all about good responsabilities, effective categorization and good abstractions. Sometimes abstraction can be improved by removing state from object and sometimes abstraction can be improved by making object stateful.
Module splitting by "concept"? What does that even mean? An entire "app" is a "concept." This suggestion doesn't get us anywhere closer to an answer about when to split things up. The reality is that there are many reasons to split code up, but if we MUST boil it down to a single rule, it's that we split it up in whatever way improves maintainability.
No, he means human organisation, i.e. Conway's Law. It's inevitable: th-cam.com/video/5IUj1EZwpJY/w-d-xo.html
Codebase structure actually does mirror the team structure of the organisation. Splitting modules based on teams may sounds weird, but it ends up happening even if you don't explicitly do it.
conways law always seems to apply !
The obvious example is microservices, so long as you don't make a distributed monolith by mistake.
That's where good OOP goes down the drain...
Codebase should reflect the problem you are trying to solve or the world your problem resides in
The simple answer is Rich Hickey answer, OO is bad because it ties state + behaviour + time in a single construct. Those are 3 separated concepts and should be treated separately. In my experience, OO leads to much more boiler place than FP. FP has its caveats if the language it's attempting some kind of purity. But most of the time it's just data (map, sets, list, vectors) and functions that produce new values, abstractions like objects get in the way, because you need to create more things (functions, classes, wrapper) to simple work with an existing object. While if you have your data separated from the behaviour you can reuse both the data and the behaviour. You can still make stuff that sucks, but at least has to return some datatype that you can work with however you want, instead of an object, that has it's methods saying how it likes to handled.
That may be true, but in a multi paradigm language like C++ the idea of objects or OOP there isn't so much the abstraction that "everything has to be an object", but more on lines with the robustness of the language that allows the programmer to create an object that becomes or acts like a new data type where the compiler will treat them like a built in type. You can create a class or a struct with members - fields that could either have public or private access and yet when another uses that object as a type, you can use it with existing operators such as any of the arithmetic, comparison, or even bitshift operators like streaming the contents of a class into a stringstream object that can later be piped to either the console, to a file, or some other output device. Not all OOP is bad, but not all of it is good either. Some languages where "everything has to be a class object" is a different story. Sometimes having just basic primitives that can be acted on is just good enough.
Reference to Rich = upvote
@@skilz8098 Your making the classic mistake of confusing OO with defining new datatypes by composing other datatypes.
"Not OO" doesn't mean you exclusively work with primitives. In fact, in "Not OO" you tend to define alot more datatypes than in OO the OO world.
C has structs, Fortran has structs too. Most functional languages don't just have structs / records, but extremely rich type systems that allow for much more expressive data modeling than OO languages.
What separates OO from other types of programming are class hiearchies via inheritance, data encapsulation and coupling datatypes with behaviour.
@@skilz8098 but
You have apparently only worked in an environment where OO was done wrong. Read Design Patterns, the Model-View-Controller pattern for UI work separates all 3 of state, behavior, and time up into 3 different objects that are intermingled. The strategy pattern implicitly states that the object that holds the strategy object contains the state and the strategy object determines behavior. When your code has well designed OO principles put in as part of the coding standards, and he people adding to the code understand those patterns, and when to and not to use them, OO is far better than functional or procedural programming for anything with more than 5,000 lines of code. To do this requires regular code reviews, not for effectiveness of the code, but to make sure it follows the coding standards. Once you step out of a pure OO approach in the code base, the code base becomes a monster to maintain, but while you are writing pure OO it is far better than any other style of programming. To get the skills to program pure OO takes a couple of years to develop after college with pair programming with a senior developer, and no company is going to want to pay for that with the turnover in industry these days.
the only thing i hate about oop is the inheritance, when it comes to do unit test is so painful to do it. So the alternative to inheritance is the composition that i like btw.
It sounded to me like “OOP conflates data types and modules” is talking about Java’s only module type being a Class, and the awkward class-with-private-do-nothing-constructor-and-static-methods thing you have to do for free functions. That could also just be the constant pain I feel from working with Java in my day job, and only Java 8 at that.
Um, packages would like a word with you! There's even package private methods, which mean classes in the same package can call the method, but classes outside of the package can't.
@@bobbycrosby9765 those are useful in their own way, but not so much for general utility and helper functions.
The inability to create a standalone functions or variables in Java drives me insane. Thank god for Kotlin
@@bobbycrosby9765 What does package-private have to do with not being able to define functions without first finding/coming up with some class to put them in? In C++ for example, you can start a file by just declaring and defining some functions, without needing to think about namespaces, classes, or types. Then, when it makes sense, you can define some type(s) with `struct`, wrap it all up in one or more `namespace`s (which may be considered a "[sub]module" or "[sub]context"), and converting `struct`s to `class`es as they grow if you want to split out interfaces or bundle data/state and functions as objects. In Java (as far as I know), you're forced to do all of that from the very start rather than introducing/letting use of those tools emerge when it makes sense to do so; hence "the awkward class-with-private-do-nothing-constructor-and-static-methods thing you have to do for free functions".
If you're writing small, single-threaded command line programs, it might seem like a small difference, but my brain personally works a lot better when I can gradually ramp up to classes, and having that control makes a huge difference when concurrency comes into the mix.
@@ChaoticTrack They said classes were their only modules. That is flatly false, you can look at packages as modules, and you have things like package private to facilitate keeping stuff private to your module (the package) so no code outside the module (aka other packages) can see it.
I'm pretty much a lua developer (technically, what I write is a modern dialect of lisp called fennel, it's fairly similar to clojure but with lua rather than java)
In lua, you don't actually have OOP, but you can replicate many of the same features and functionality
As someone who started out learning java, it's a bit of an experience, but it's really nice and quite enjoyable
Tables are basically prototype programming like Javascript, don't lie to yourself, working in lua ends up you making oop without having oop...
Which is fine thou, la is hated by so many people, but it's pretty decent. It's just evolving slower compared to other languages, but if r compare lua 5 and ES5 (released around the same time) lua is way better.
Now the problem with lua is how it's stuck I'n that past (well lua evolved, but not much) while ES started to do giant jumps between versions.
And I'm the case of lua, a lot of people will forever be stuck in lua 5.1 because of luaGIT. Which causes more fragmentation between lua users, luarocks and it kinda handicaps the future of lua.
@@rex_melynas Practically speaking, LuaJIT isn't stuck at Lua 5.1 but instead branched off from Lua 5.1. The real hit to it in terms of usability is the author rejecting the concept of releases, which is basically just LuaJIT grabbing one of JS's problems (feature uncertainty) for no reason at all. Rolling releases are fine when it's just technical details changing, but there're some pretty big changes all under the same version name...
Julia is designed with separation of modules and data types and its very good. It uses multiple dispatch to get the functionality of class methods, but without conflating modules and data types.
it just feels good.
Julia is a good language. Wish it can replace Go, but it's ecosystem is so far removed from the greater software engineering ecosystem that I don't know where the language goes...
Brian Will's OOP videos are my favorite. I rewatch them regularly
Same. The guy really seems to know what he's talking about.
i really liked it, i was confused a bit by some of the terminology he was using though :)
What are these videos?
@@ThePrimeTimeagen definitely watch the OG "Object-Oriented Programming is Bad" one, that's the legend
@@panstromek Or OOP is Embarrassing, the sequel
"Functional Code is just Procedural Code with functions..."
As an F# dev, this is 100% how I write my code.
*High fives in Scala*
I would amend that to "functional code is just procedural code with functions and clunky category theory abstractions".
These videos are awesome! I'm so glad there's a voice of reason in the TH-cam Dev world
OOP hater is a gross oversimplification of Brian Will's talks
Everytime someone talks about OO I would like them to clarify upfront: which style of OO? cuz the value passing message-passing style of Smalltalk and Erlang are pretty functional and nice
I was searching for this comment, In my company we use Smalltalk for everything and not even the decision behind the language are very nice but also the common IDE that the smalltalk dialects have are so good. It's very easy to navigate between messages and classes, the only disadvantage is the version control system, Pharo has the possibility of using Git, but the common is to use ENVY.
You mentioned it on some points. But it feels like there is a miss on simple concepts like DRY, KISS, YAGNI and especially SOLID which makes any programming style easier to execute. I saw terrible OO code but refactoring the whole project taught me alot. The result was a clean, well structured Java project with akka, sql etc. Less coupled layers between database ORM, the services, actors and the endpoints. And one of the most awful things I saw there was a huge monstrosity of inheritance and tests. Tests inheriting from other tests. omg. I purged it completely and made proper unit and/or integration tests for the same use cases. It's always about the real problem that requires a proper solution. pragmatism is key. OOP or not, people can f*up anything if they do not know how to use tools. It is not the tools fault. At least not always.
OO is programming with objects that communicate via messages. Pretty much nobody actually does that in practice, people call anything OO as long as it has encapsulation and dynamic polymorphism, neither of which are unique to OO. Static polymorphism isn't really an OO concept at all, but is IMHO preferable to dynamic polymorphism when applicable; the only reason we don't use it more often is that most programming languages just straight up don't have a way to even express static polymorphism, or at least not in a way that isn't extremely cumbersome to use. And of course, most things don't need to be polymorphic at all.
Exactly this.
math teacher called they want you to isolate your variables when writing functions or else you fail the test. 'polymorphism' is not a variable.
You need an OO language to properly translate a domain driven model into code. I have been a developer for more than 10 years by now, and when you can get rid of your perceptions you start seeing different programing paradigms, technologies, frameworks, languages as tools. Choose the right tool for the task.
So functional DDD is a joke to you?
Architects are perfect for big rusty corporations who like to set 10 meetings to triage the epics to handle a change request for a json field that needs to be passed down 3 microservices with an ETA of 4 sprints
The more data you're working with the more OO matters. Overengineering your classes because "patterns" is just layers and layers of crap. I call anything that uses classes OO. Even if it is mostly just organizing data structures and not complex relationships between objects.
I have had the wonderful opportunity to pick tools and replace a number of redundant systems with one good one. I picked Nim for C compatibility, native speed, and its ridiculous modeling power. Coming from the functional world, I love its defaults: Parameters are immutable unless `var`, and procs have their own context. This makes the functional/procedural style of building modules of related functions, passing data to each other to produce new outputs, or more often down a pyramid of calls that construct a more complete bundle of functionality, crystal clear. It's beautiful. I have one module that does C++/Java-style OO and it fits the purpose beautifully, but NOTHING outside that module is modeled that way. My only headaches in Nim have not been modeling problems (with enough thought it deftly guides you to the correct answer), they've all been dealing with decades-old anachronisms of the C toolchain, which Nim uses. I love the speed and compatibility though. Linking static binaries with musl-libc feels *very* good.
People just need an enemy to justify their bad decisions, laziness and sometimes their jobs, right now is OOP, classes are now considered harmful and perfectly working code should be discarded and rewritten to be more pure.
I prefer to mantain software made with OOP than the unholy mess that is haskell, untyped spagetti javascript and non performant python code.
Unfair generalisation. I don't like Haskell syntax, yes I agree it's bad but I like the concepts it comes with, programming in haskell just hit different to programming in Java or PHP. Think of it like music different people different tastes but unlike music programming paradigms feel more like religion. Also it's hard to write good functional code I've seen a lot of functional that's just procedural code.
@@ea_naseer when you have a 10 years old codebase in java, or C++ that works, and suddenly a religious zealot wants to throw the code because OOP BAD i take offense and defend the old and ugly code over the more pure and ideal future, in you analogy, im more like an atheist, what works works, most languages now are multiparadigm anyway.
This Brian Will video resembles an exposition of the Clean Code Architecture. The data type are separated from the logic and the state management
interesting take
22:10 Extending EventEmitter works well but I prefer using Signals (an event dispatch/registration model pioneered by Robert Penner - same guy who gave us easing equations in Flash/ActionScript back in the day).
There is an NPM package called mini-signals that's a fairly good implementation of the Signals pattern. The Signals pattern embraces the old adage 'Favor Composition Over Inheritance". Instead of extending EventEmitter, you create a dedicated Signals object that has dispatch and addLIstener methods.
Decoupling event dispatch and listener registration from its client allows multiple clients to independently leverage the same signal. Signals are pretty flexible and allow for all sorts of messaging patterns (message buses are amongst my favorite).
The only time I found inheritance really usefull was when I actually wanted to override a class behaviour. I worked a lot with the Laravel framework and when I needed some internal class to work differently, from the framework or some external library, I could easily extend the class, override the methods I cared about and then tell the dependency injection to use my class over the default one, or even, when to use my class over the default one, pretty neat.
This is the perfect scenario for Inheritance - This is how we guide the users of our SDK/framework (both internal and external) - 'If you need to change behaviour of Class XYZ then you can override methods ABC, DEF.' Inheritance needs to be used judiciously, but in the right place it is an extremely powerful tool.
Used inheritance when creating a flat matrix class in typescript; had a Readonly version as the base and a regular, mutable version that inherits from Readonly but has a .set function. Cases like these are the only times i use inheritance, but they are very powerful.
Yes, this is one of the two good uses of inheritance. The other one is creating base classes for one kind of classes, for example models or controllers. Then you can make the classes much smaller and focus on the important logic.
Inheritance is fine, the problem is it is extremely easy to exploit and most devs are dumb and will exploit it unless properly taught how to use it or kept in check by their seniors.
OOP is good, but with an A$$ The Rick. No honestly, it's good in some domains like line of business stuff, but only if you heavily use FP concepts also. OOP without FP to me is like the old OLD way to do OOP, with tight coupling everywhere, unexpected error states easily introduced when something is (so easily) not used as it was supposed to be used, super deep levels of abstractions that are most of the times not even needed, etc. But OOP has gotten better in recent times, especially with modern versions of their languages and accompanying frameworks and architectures... it has to.
OOP is kind of all about lose coupling - all early OOP languages had came with tools that allow achieving lose coupling pretty well. The "old OOP" style code that everyone hates in fact often is not OOP but procedural style code with extra syntax sugar of classes and inheritance - basically people who used to know how to write procedural code - learned about oop from idiotic books "head first OOP" or similar that the only thing it teaches is how to organize your pets - cats and dogs - under common base class 'animal'. Little who took time to read into more serious literature on OOP.
OOP is designed for simulations. Look at the origin. Do you need a thousand slightly different airplanes?
@@carlsjr7975 This. I tend to be anti-OOP, but I feel like OOP shines when you're dealing with actual _objects_ in the real-world sense.
@@carlsjr7975 not really. OOP emerged simultaneously in multitude of different environments.
@@sk-sm9sh simula. Look it up.
OOP is nonsense not based on anything except some vague ideas from 90s. Everyone means their own thing when talks about OOP.
This is reminding me to read On the Criteria to be Used in Decomposing Systems into Modules, a 1972 paper by D.L. Parnas, in which he argues that one very effective criteria is localizing change. I think this is where you get things like the Open Closed Principle. OOP is optimized for being able to modify the behavior of a system with minimal changes to existing code, if you do it right. But most people write code that is constantly changed or refactored instead of adding classes that override existing behavior, so in those cases OOP provides little if any benefit.
Really depends what you mean by OO. For re-usable data structures mixed into procedural code, OO is great. No issues. For de-coupling complex systems a la Spring, it's quite a major different idea of OO.
This. I use classes for low level data structures and APIs, but anything to do with logic, orchestration, high level abstraction is all composable functions.
If you reviewed the Mario Kart 64 decomp source code when it gets completed I think you would find it's lack of good programming hilarious. No real memory management (actual memory management funcs unused), manually written DMA calls everytime instead of a manager. Copypasta actor behaviour code. Inconsistent use of array + offset and array[offset]. Code in random places that do not belong. Overly long functions, fall-through switch cases, unused variables, etc.
I like having methods on objects. I don't like len( arr ). I do like arr.length. I like "I'm a string and I know how to do stuff".contains("stuff"), button.on('click', (evt) => doAction() ), list.find( (x) => x.hereIAm ). My IDE helps me remember the options I have when I'm given a specific type (dynamic languages mess this up, but it's ok). This is OO and it works really well, and it's powerful. If you decide EVERYTHING MUST BE PROCEDURAL!!!! You have to give up all of that, and I don't want to code in XLib or Windows C Lib style code ever ever ever again. Anyone who is convinced procedural is better doesn't remember the days before OO, and what a nightmare those were.
Nothing better than full FP stack. Elm on the frontend, Haskell on the backend. Tokioooooooo
it sucks what happened to elm
I've always had an issue with architects who don't do the things they architect. And this doesnt apply only to development. It's the same when we do IaC.
Programmers have always been very dogmatic about ways of doing things. Its easy to see that our egos are waaaay tf out of control.
The first time I heard his rant about "OO bad!" I picked up on his bias immediately.
You can go watch his original video and pay attention to when he says things like "why do this, when you can just do this?".
Thats just bias, a preferred way of doing things. Because ANYONE can just as easily assert the same thing in the opposite direction.
And I guess having a youtube channel makes people feel self-important, to compound the average developer's out of control ego
The good parts of inheritance can be had with Haskell type classes. It’s not a set-subset relationship. It’s more like “this is an instance of this principle, here is how”, with the “how” filling in any bridging functions not defined by the type class. So you can implement many different type classes, in a way more flexible than that allowed by the set-subset relationship of inheritance. Functional master race
At the end you answered someone's question about coding, seniority and architects. You've mentioned that your team was fully seniors and I have similar experience on that situation; everyone was able to take responsibility of the architecture together and everyone produced code for it.
Separate architects and/or less-coding seniors might be more common on teams with significant portion of junior developers. In that scenario someone more experienced needs to take care of the big picture and also spend more time directing and helping the juniors.
I've done so much OO since I started programming I really struggle to break away from it, most problems seem so easy to handle in OO for me. I would love to learn more functional programming and procedural but it's so frustrating to not be able to come up with the solution quickly and to relearn everything. I guess it just made me impatient
If done correctly OOP has its uses and can be quite a powerful tool in the tool box, however it is not the answer to everything. I think it honestly depends on the task at hand and what is required of it. I'm not a "big promoter" of it nor am I "against" it. I think that to know what OOP is and how to properly implement it and to properly use it is more important than the debate for or against it.
Best comment ever.
On the game I'm making at home, the behavior class was divided into two files. The class itself is the state module, and the other file is strictly for organizing specific behaviors as extension functions of the individual behaviors, that can then be swapped in as delegates. While not identical to how he suggests, it does seem to be an implementation of separating the logic from the state. (Working in Unity, I can't help but be in object hell if I don't do it right.)
But at this point wouldn't you be better off just writing procedural code, because what you are describing doesn't seem much different than what I write in C. I have a struct with data, and functions that operates ton that data both are separated.
@@pierreollivier1 my reason for dividing them was mostly for organizational purposes. That being said, I have completely reworked my behavior class to work more like a finite state machine for ease of use, as of a few weeks ago.
@@pirateskeleton7828 I love finite state machine, I use it a lot for prototyping actually, It makes the incremental process of discovery so easy because you don't need to build a lot of infrastructures for it to work, at first your FSM can work on one case, than as you extend it more enums and functions pointers you get closer and closer to the end goal. On top of that the debugging of FSM is amazingly trivial.
"Utils" is generally where ACTUALLY reusable code lives.
Couldn't be more true, I literally never reused any other kind of code between two projects.
The difference between procedural and functional programming is that functional programming has a big emphasis on pure functions as much as possible, which means you don't have any guarantees on the order in which things are evaluated, for example, in opposition to the imperative declarations you would see in a procedural software.
So basically, programming with functions != functional programming
31:18 "it's hard to take anything concrete out of it" - how I feel about every such video on coding concepts that don't include a single code example
I'm sorry you had to deal with bad architects.
I do know some architects that should of been moved to a management position.
I understand the frustration.
For reference good architects at our firm are :
leaders bridging the product owner and SM/PM with the team;
designers of solutions; ( Meaning we make the draft and evolve it with our team to something production worthy and keep track of changes to architecture. )
participate in the writing and validation of code.
We also write the high level and detailed architectural documents as a reference for other teams and update them.
Finally, architects almost never work alone.
They hold architectural boards with other architects ( data, security, integration, infra, etc. ), explain findings and get them approved by other architects.
Maybe you can have the software engineers do all that.
But in my experience, lots of stuff get pushed to the side in favor of just implementing.
That can lead to spaghetti code, architecture and technical debt.
I'm such a proponent of OOP, I don't watch bare TH-cam videos, but only ones that are boxed in reaction videos 3 times the size
I have never been able to find a use for interfaces in TypeScript. But I do use them in Java for common methods shared between different classes.
It can be used to explain the colleagues or future you, what a variable contains instead of using any.
25:20
Dont (hadnt) watch vids like this... so hearing someone say what ive been struggling with in private, word for word, is a relief. 😌
Every time I see these OOP critiques, I'm reminded of Borachio's retort to Conrade:
"Seest thou not, I say, what a deformed thief this fashion is? how giddily a’ turns about all the hot bloods between fourteen and five-and-thirty?"
I develop a lot of C++ code in and around AUTOSAR. First, we have a lot of hardware-specific registers. With inheritance, you can create a hierarchy of datatypes you can check against in tests or runtime. Also, we have a lot of virtual function classes. With the proper settings those force you to implement low-level functions for any "is-a" relationship, that will use a specific interface from application to hardware. It`s generally not the worst idea to separate a technical system from its physical models and describe each of them in a class.
There is this really interesting talk by Evan Czaplicki, “The Life of a File” where he talks, among other things, how the acceptable size of your file is dependent on things like the language you are using. That’s be an interesting talk to watch here.
If your Utils package has 900 classes, it is time to split that package up. Single Responsibility Principle shouldn't just apply to classes/modules. It should also apply to packages.
And if you end up with too many packages, you should organize them into hierarchies of packages that make sense.
It is Single Responsibility Principle all the way down.
This is a classic academic vs practical conflic. A module is an abstract academic concept as the guy discusses it. It could be one function inside a larger file, it could be a folder with many files. All it means is some set of conceptually grouped together code. So like that example of the one function that doesn't fit the concept, that just means that one function is its own module.
OO is great. Just being able to separate implementation details from APIs easily is amazing enough. Having custom datatypes, great! Inheritance? Not quite so great, but it makes sense often enough. It's a tool to be used when required, not a mandate to use it. The same for the whole OO concept. You wanna have a game and represent a tank? Just make a tank object. You want your tank to have different weapons? Aggregate. I don't see a problem with that at all. I wanna see the the guy trying to use a functional language like haskell for that. That's just insane. Hiding your datamembers behind a well defined, public API just makes sense. I don't understand all those c people shoving everything into a global namespace and then having to come up with ridiculous function names like scanf, sscanf, sscanf_s and whatever other permutation of leading s, trailing _s and middle n there are.
Neither implementation hiding or data types is a concept introduced by OOP. No one seems to agree what OOP is, but most definitions uses the "four pillars of OOP" as a base point. In the "four pillars of OOP", inheritance and polymorphism are the only really unique features of OOP. The remaining pillars, encapsulation and abstraction, are not unique to OOP what so ever. Even C can make implementation details private using the static keyword.
Using your tank example, the obvious approach would be to use some sort of structured data, e.g. a struct in C. This has existed decades before OOP was invented. What OOP brings to the table would be to introduce a parent class GameObject and a subclass for each tank. Then using polymorphism to "abstract" parts of the codebase, only to struggle 1 year later when you want to implement that North Korean tank that doesn't neatly fit the existing abstractions of tanks.
OOP proponents like you are essentially saying "OOP is good if you don't use the defining features of OOP"
@@tapwater424 sure, you can make your tank struct in c, but you can't put the fire method or the move method into it, and instead you come up with ridiculous names and insane parameter passing. Anyone can just do whatever to your tank objects and you have to do some debugging to find out why all your tanks suddenly have the same fuel value because the bug can literally be anywhere. How are you going to give your tanks sabot shells and heat shells at the same time into the same ammo rack? What if you want to add hesh shells later? With a complicated mess of logic, that's how.
Nevermind all the other advantages of c++ over c like templates or the standard library. OOP gives you options. I don't care what OOP is or what you call it or what people agree on what it is, I care about what I can do with the language. And if I can allocate resources that deallocate themselves automatically with no or minimal overhead, then that's something that c can't do. Just because a shovel has a sharp edge doesn't mean that you go into the woods with it to chop trees. c++ is 99% compatible with c. I don't see why you wouldn't use it over c, even if you don't use classes, it just gives you more options. classes are just yet another tool to use if your problem calls for it.
Also if that north korean tank doesn't fit the definition of a tank, then it's not a tank. And I know there are some whacky tank designs, but a tank generally is a mobile, armored box with a gun. You either introduce an intermediate class, like standard tank or your derive your north korean special tank from vehicle instead because it's not a tank. btw, north korea just uses old russian and chinese crap. the kind of junk russia is pulling out of it's storage right now. Stuff that is very much what you think a tank is. There are much more stranger designs.
@@FalcoGer This is not a C++ vs C debate. I just used C as an example because it is a language invented before OOP became a thing. The fact that C++ has useful features is irrelevant.
And your perception of C has no basis in reality. Tons of useful software has been written in C. It has been used for video games, operating systems, version control. Really any software you can imagine.
@@tapwater424FalcoGer is completely right and your tank example was exactly one that explodes in your face later in development if written in a non-OOP language like C.
The fact that there is software like this exists written in C, doesn't mean it's anywhere close to being as maintainable as similar software written in an OOP language like C++.
If the Tank interface in your example doesn't satisfy what you need for the North Korean tank, guess what, you can and should refactor it since there is an obvious design flaw then.
The point is that you can actually refactor it locally and in every class that implements the Tank interface with your IDE and Compiler giving you errors when you missed something instead of having to look around in 10k loc to check where your Tank struct was passed around as void* and dereferenced in places you didn't even know existed before touching this.
@@tapwater424I disagree that inheritance and polymorphism are the two core pillars of oop, encapsulation and polymorphism is. The fundamental concepts of oop is that by associating behaviour with data (i.e. encapsulation) doing the same thing to an object will produce different behaviour dependant on the data (i.e. polymorphism). You can have good OO using only interfaces and no inheritance.
Appreciate the long form videos man. I'm just learning to code, for personal interest mainly but potentially to assist in a future career change. Your content is helping mold and shape as well as contextualize what I'm learning in the courses I'm taking online. Keep up the good work!
Global state mutation is just unmaintainable in any non trivial codebase, so no, functional is not the same as procedural.(Tbh, I'm not against a "global" state in a module/dll if it offers a reasonable performace gain in a hot path, but that's just a possible optimization if it doesn't affect the whole program)
OO might work if you can predict the future with 100% accuracy
23:02 Atleast in Rust, if the entire benefit you want is typing two words to get a benefit instead of typing a lot of words, a blanket implementation using #[derive(EventEmitter)] wouldn't be too bad.
i am curious how this would work because you need to add properties to the struct (something to track the callbacks) and i assume you don't want them ackshually showing up in the LSP (unless you do)
@@ThePrimeTimeagen ez bro just use a singleton
@@ThePrimeTimeagen I can't say I'm knowledgeable about this, but can't this be done by just having a private EventEmitter data member (which obviously itself has public fields for manipulation inside impl blocks)? This way the LSP would know not to display its details and methods. But still, there are problems with this.
1) Actually modifying the struct. Derive macros don't allow changing the struct. For that, you require attribute macros. Syntactically they're similar, but feel "special"/"magic" to me for some reason.
2) This will change the repr of the struct and if something somewhere is relying on it, it will break.
3) Debug printing will get ugly, since now an object irrelevant to your main functioning is filling up your view space. This part actually bothers me a lot more.
Oh, well: OOP. What are the three major goals of any larger SW project / codebase? Correctness, performance and testability. There are other aspects of course like reliability and maintainability.
One of the major strategies for correctness is keeping errors "local", not spreading out into 100 files or modules. That's why we have modular programming: first functions, then compound types, then classes / interfaces with data hiding, then patterns like dependency injection with lose coupling of components. This helps also with maintainability and downwards compatibility, so that new code can work with old code.
But OOP is only one paradigm of many in programming. When my 20+ years in the industry taught me one thing is, that OOP isn't per se good or bad, success in a project depends on many factors of which choice of programming language is only one of them.
finally you reacted to him but come on you left the most impressive video "Object-oriented Programming is bad"
I prefer PHP traits where CanQuack is a chunk of code that is essentially injected into the class scope, and interfaces like Quackable are contracts for polymorphism. When you combine traits and interfaces you can easily satisfy the contract without needing to create thousands of lines of boilerplate like Java.
The catch though is traits in PHP are annoying to override because you have to essentially rename but still include the default behavior to make room for the new definition. Is this something that Rust solves?
"Like Java", what decade are you in? Java might've been verbose before Java8 ten years ago, not anymore. Newer versions of the language allows you write as compact code as any other language.
@@pompiuses 2014 hoping to get my team migrated to 1.8 pretty soon.
Implicit implementation is bad because a function signature does not determine equivalent semantics.
Good OO is very close to FP in many aspects. Well constructed objects are nothing but a set of partially applied functions. Typical OO in the wild tend to be more class oriented programming than OO. And isn't a Monad a special kind of object implementing the monad interface?
C# extension methods solved the "Utils" problem
I usually have an "Extensions.cs" static class that does not need to be referenced explicitly.
Engineers have at least a high level understanding of architecture in the same way that architects have a high level understanding of engineering - in the same way as physical building - the point of an architect should be to help glue together the developers ideas to solve a problem and be flexible, like a servant leader - not act like a dictator - especially in anything that isn't pure Waterfall - and by the time it matters in Waterfall the architect has left the building anyway.
14:45 utils is a code smell in real world applications too. Organize code by type. Even if it's just string. Not like it changes much, or you need to look at all number utils and string utils at the same time.
I prefer to organize by feature at the top level and by type beneath that. I've found the inverse hard to navigate, even when e.g., using a framework where every feature will have stuff like 'ui_components', 'controllers', 'models', etc. (In that case, I start with a flat structure within the feature context/module and only start to make folders for the types if it grows to ~8+ files or if I want to hide files that I don't really care about, esp. in Java where it's one class per file) Further, I _do_ actually _want_ to see all the string utils at the same time if I want to do some generic operation on e.g., strings in my 'accounts' context when the standard library doesn't provide it. I don't want to dig through every file in my codebase (or do that in my head) to see if I've already written that logic. If you're using Vim or an IDE, you can fold all the function definitions or open the Structure menu to only see the signatures.
Why is it a code smell? And how do you fix it?
@@lengors7327 google my article "Why your folder structure sucks". Utils isn't the worst but I still avoid it
21:00 You should create an abstraction as soon as you have a perfect name for it. No sooner or later.
channeling his inner yae miko with "tell me it's not getting a little functional RIGHT HERE, RIGHT NOW"
I really enjoy OOP in Smalltalk; it’s much less pleasant in other languages.
As Primeagen says, “Fight me!”
> “Utils is a code smell.” The problem is that that statement is a statement that exists outside of practical work.
YES. Truer words have never been spoken.
They already do this in game dev, it’s called ECS: Entity-Component Systems
I think OOP is great, amazing even. The problem is that people overengineer their solutions.
if you like oop so much then why dont you override the public private polymorphic abstract pure virtual immutable protected interface class instance container manager and marry it?
Go wait and async bud
@@willtheoct Sounds more like you should marry decorator functions.
@@ci6516 So syntax sugar for creating and joining threads is now something praiseworthy?
@@CottidaeSEA pretty sure they're just called functions
this is so true, Singleton always is responsible for bad code, and slow code,
they always exist in a way which will make you cry
Casey Muratori has some arguments against private fields, namely that getters and setters should only be used if they perform some kind of processing on the underlying data and that public fields allow you to be more flexible with your testing down the line. I mostly agree but still use private fields as a way to add discipline to my implementation basically forcing myself to design a good interface rather than accessing things whenever and however I like which can eventually lead to spaghettification if I'm not careful.
I mean coding in OO is fine, but it shouldn't be the only paradigm you know.
singleton -> static class
inheritance is for stable libraries and as an easy way to have interfaces: instead of writing the parent class first, you write the parent class as an interface (pure virtual class) which takes like two seconds since you just forward declare virtual functions and common data members but don't define logic.
The poll results are not that weird. In most universities OO gets taught as the end-game content, so you leave it looking for an OO language job. And some people never leave that dark tunnel xD
@25:13 no, it is actually obvious when an abstraction is useful because it reduces the code complexity or the amount of code written substantially. Try writing something equivalent to a custom C++ container without a class and then make a class. The difference is massive--there are useful abstractions and when things get too messy they become an obvious solution.
functional code isn't just procedural code with functions. You could potentially say it's procedural code with *only* functions or better yet procedural code with *only* *pure* functions but it isn't just procedural code with functions. It has a lot in common with procedural code but procedural code is using structure to manage code OO is about message passing and I would argue has become about controlling access to shared state and functional programming is about the complete abolishment of shared state.
29:15 Architect = dictatorship
people collaborating together being architects of their own segment of the project = democracy
Memes aside, the entire process was described is like how legislation is supposed to work. Someone comes up with a new law or an edit, it gets brought to everyone, they voice their opinion, make edits and loop until all are happy. It's a very good analogy, imo
modules here is more like a folder, a group of code the fit together, so you don't get the user/role objects that need code from each other thing, but user and roles is in the same module but product/catgories may not be in the user/role module as they don't fit together.
I really don't get that part on pure functions writing in files. I consider pure functions those which have no side effects, writing files do have side effects. What got me was OOP "promotes small abstractions". I remember Pesce writing something like juniors understand better (and tend to build) simple structures with complex logic, seniors prefer complex structures with simple logic. Maybe the latter requires higher skills than the former but I wouldn't consider it wrong just because it's not something anyone can achieve. The former, in this pov, looks more like a white flag.
If modules are singletons but as an instance of a class, aren't they just...globals in a namespace?
"..in almost all cases our state modules are singletons" - Prime starts shaking head wildly :'D
I would argue modern OO with interfaces is all-in-all a good thing and for more junior devs it helps to learn the right way to reason about code. Rust's type system and impl-s are far more natural to me now, but 3 years ago I was not a mature developer enough to understand it in depth.
Man just reinvented Finite State Machine design (digital logic in hardware), but as a software paradigm.
(Going off th 2 module types at start anyway)
The problem with a "utils" folder is the generic name as it doesn't convey the content. Could have been named "stuff". The promise explode function named, while being a utility function, does have a very specific function and should therefor be in a folder denoting its usage, such as "promises/interop".
hey hey. the only use of oop is that it can help visualising your code. but honestly we all only use it cause it sounds satisfying to say. oops
Not sure why this entire generation of coders hate OOP. I love OOP because it increases project organization, abstraction, and meaningful code. Things may be turning more to composables, but still OOP has its place. Dealing with very large projects I will always choose OOP.
Agreed. It's not always good, but it often is.
The big problem with OOP is that so many people are dogmatic about it and think it's always the right approach.
I like to mix the two together with classes, properties, interfaces and objects from OOP and the methods itself written in a more functional style using C# linq/java streams. OOP makes organization super easy
Yeah OOP has a lot of good principles to it that will really help your code in general once you work on anything of meaningful size. Honestly the guys that hate OOP I really have to wonder if they do nothing but small programs. I can't imagine any kind of even moderate-sized application being done in all imperative style without having a code base that's a huge mess. It also works very well when you factor in any kind of IDE-like behavior in your editor, because you can take any object and see the auto-complete for the methods of that object. So whether you're dealing with a string, a vector or some custom object in any codebase you can always get an idea of what things that object can do by its methods.
And to be honest, all sufficiently complex programming starts to look more OOP. Even C's standard library with fopen, fclose, fprintf, etc are all essentially primitive OOP just without the handy OOP notation and associations. You have this object that's holding a state, you largely keep the implementation details unimportant and just hand the user functions that do operations with that object and translate it into basic types without ever having them need to know what's going on behind the scenes. It's just a lot handier if you have a real File class, so you know instantly where to check it for methods. Not to mention all the other features like RAII you can then build into it to not have to worry quite as much about cleaning it up.
@@taragnor Yup! Exactly. OOP isn't the end all be all, but in general OOP is important when it comes to implementing specific design patterns like MVC, dependency injection, etc. Especially useful when using external libraries/modules. Also makes documentation easier, and code reusability. There are just WAY too many benefits to it.
Whats even funnier is that all these people who are against OOP don't even realize they are benefitting from its practice. Like all these newer generation React devs... Like you guys realize that components are still just objects right?
what do project organization and OOP have to do with eachother?
Brian is peak Dunning-Kruger, tried to watch one of her OOP hate videos and he can’t write it so he can’t understand it😂
In general, that's a refreshing take, while I keep hearing the architects where I work being fascinated by DDD (barf)
An even more honest title for the video would have been "Computer snob reinvents the wheel"
I organize my modules by dependencies. If you ever want to reuse code, dependency management is the game.
I guess by collaboration in teams they mean teams with a large scope. Say with a OS, teams working on user level features will be dependant on a clean public interface from the shell team.
I see his "state modules" as "services".
That just makes far more sense to me, but hopefully I'm not just misunderstanding what he means by "module".