Тёмный

Abstraction Can Make Your Code Worse 

CodeAesthetic
Подписаться 372 тыс.
Просмотров 609 тыс.
50% 1

Support me on / codeaesthetic . Access to code examples, discord, song names and more at / codeaesthetic
Adding abstraction to your code always feels like the right thing to do. But when you add abstraction, you add coupling which can often undermine the value of the abstraction.
#programming #code #design #softwaredesign

Опубликовано:

 

19 ноя 2022

Поделиться:

Ссылка:

Скачать:

Готовим ссылку...

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 1,1 тыс.   
@charlestolley2294
@charlestolley2294 Год назад
The one design principle every programmer learns is "Don't Repeat Yourself", and it's not very helpful on its own. I wouldn't broadly say that abstraction increases coupling, but you can't make design decisions simply by looking for repeated code. Your abstractions should represent real concepts in the system that allow you to speak about them using natural language. Done properly, this actually decreases coupling, in a way that not only makes it easy to add new behavior, but also makes it easy to isolate each component for testing.
@matias-eduardo
@matias-eduardo Год назад
There's an interesting constraint that you can place on yourself that makes DRY good: don't use functions to DRY up your code. It sounds extremely counter-intuitive, but it forces the coder to think about queuing things up and doing them all at once further down the flow. There's a good talk by Mike Acton that can help understand the value in doing this.
@jalendixon5894
@jalendixon5894 Год назад
Idk what you eat for breakfast but I want some.
@zostaw9421
@zostaw9421 Год назад
@@matias-eduardo do you remember the title by any chance? I would love to listen!
@matias-eduardo
@matias-eduardo Год назад
@@zostaw9421 ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-rX0ItVEVjHc.html
@matias-eduardo
@matias-eduardo Год назад
@@zostaw9421 I revisit it every year and learn something new. Everything from min 40+ is timeless.
@sfulibarri
@sfulibarri Год назад
To me this is the most significant difference between new programmers and very experienced ones. Being able to identify and choose the most valuable abstractions in a given context is what drives business value from raw development. Its troublesome because the emphasis on OOP in programming education leads many newer devs to implicitly value abstraction above all while not understanding the complexity they may be incurring. Choosing the right abstractions takes a lot of experience and must be informed by context. Even an apparently 'correct' abstraction may not be the right choice when considering the business context, general experience of the team, and even delivery deadlines; software engineering doesn't happen in a vacuum.
@ecm9251
@ecm9251 Год назад
I don't believe experience will make a programmer being capable of using abstraction. It takes abstract thinking and it seems like a lot of people struggle with this. Most programmers might never in their live be able to use abstract programming, no matter how long they try. Some will be able to use it with very little experience in programming. It might sound harsh, but we all know that people are different and abstract programming isn't for everyone.
@sfulibarri
@sfulibarri Год назад
@@ecm9251 Abstraction is one of the 3 basic mechanisms of any programming language. In most common languages once a programmer learns how to write functions or classes they are already using abstraction. Programmers may be better or worse at leveraging abstraction to create better software but the idea that some programmers are somehow incapable of one of the most fundamental aspects of the craft is absurd.
@TheIllerX
@TheIllerX Год назад
Well written. Especially in those first classes in OOP, one often get the impression that everything should be an abstraction and you should do inheritance structures of everything just because you can. Very little is said about avoiding to create couplings and reducing complexity of your code.
@ecm9251
@ecm9251 Год назад
@@sfulibarri Your comment is placed under a video that is considered popular. It's popular because a lot of programmers are struggeling with this basic case of a basic concept.
@sfulibarri
@sfulibarri Год назад
@@ecm9251 Well the video is about one of the pitfalls of making unnecessary abstractions, not about abstraction itself, a person searching for 'what is abstraction in programming' is probably not watching this video. I agree that some may struggle with abstraction in the sense that they may make abstractions that are counterproductive, but you seem to be suggesting that 'most' programmers fail to understand and/or use abstraction at all which is just categorically untrue; any programmer who has ever extracted a few lines of code into a function has used abstraction. In my career I have worked with many jr developers and I never met one who simply could not grasp how or why they should extract a function or class. I maintain that the difficulty of abstraction comes from knowing how and when to use it effectively, not understanding the foundational concept itself.
@AleksanderFimreite
@AleksanderFimreite Год назад
In my experience. The fun thing about programming, is that it does not matter how much you are aware of. Or how many suggestions you have gotten. You will still end up making regretable mistakes reguardless. Whether this is poorly abstracted elements or just flat out wrong architechture, it always happens. I struggle more with accepting that my code is good enough and actually finish the project when I know I could improve it. Rather than the coupling issues themselves.
@dennisjungbauer4467
@dennisjungbauer4467 Год назад
I think clear requirements and thinking a bit about what the most fitting implementation seems to be before starting with it can help quite a bit. If you're working in a team and use a ticket system it's rather important in my experience to think tickets through and question them as well before working on the implementation and noticing logical inconsistencies or potential problems that were not touched upon. Thinking a bit about the near future/setting boundaries can help as well finding the balance between abstraction and a more concise/simple but tight code. You can't prevent needing to make changes later on or rewriting some stuff mid-implementation, though, that's par of the course.
@AleksanderFimreite
@AleksanderFimreite Год назад
​@@dennisjungbauer4467 I'm guessing there also are significant differences between different branches of programming. I work with a small team of game devs. And in the beginning our programmers all discuss togather and agree on benefits and acceptable limits of a system. And then about half a year into development, it turns out our current system did not feel good enough to players. So the designers modify the core specifications 1 month before delivery. Usually with too major differences to justify just a rework. So we end up having to do a half-assed system temporarily and make it from scratch again later. So there are definitely also deminishing returns on planning ahead in my field. We basiclly just do uses imaginable expansions for the current system. Reworks are too unpredictable to support sadly. While I can imagine in system applications, the core behaviour is much more set in stone from the get go, and the front and back end can be dealt with separately.
@SI0AX
@SI0AX Год назад
@@AleksanderFimreite IMO, reworks should only be done if modifying the existing code will take a lot longer than doing a rework in a different, more modern/efficient language or tool.
@DigitalViscosity
@DigitalViscosity Год назад
I'm just glad we are out of abstraction hell of 2010, my god every API and codebase back then.
@BramHeerebout
@BramHeerebout Год назад
I remember when I was taught programming long a ago, one of the first thing the professor told us is that sometimes, maybe often, it is better to start over. Learn to accept that. [edit] And to add to that, abstract classes, especially does that are written with reusability in mind tend to take some time to polish. However, I learned in practice that code often has an expiration date. This 'saver' class is a good example. The xml code gets ditched for json. Don't think the json will last forever. It too will be obsolete. And unless you are working on something like the space shuttle, it won't take decades.
@TinusBruins
@TinusBruins Год назад
In my experience abstraction isn't about removing repetition, though it does work for it. I think its most important job is to remove dependances. As is in your example with the IntervalSaver. The actual saving might happen in a different library. And you don't want the main code to be dependent on all the possibilities of saving.
@lachlanstanding7386
@lachlanstanding7386 Год назад
exactly, the class becomes reliant on an interface instead of the actual implementation, thus *reducing* coupling
@StephensCrazyHour
@StephensCrazyHour Год назад
This. Proper dependency extraction can lead to code that is simple to test. Testable code is good code.
@rybakostis
@rybakostis Год назад
100%
@leonardomangano6861
@leonardomangano6861 Год назад
I am mostly against this. Creating interfaces that won't have more than one implementation at the same time are a waste. Except in cases where the code is talking to an external system like an API or a DB. Removing dependencies just to create a mocking hell is not a good approach to me. Test the unit with its dependencies every time you can
@charlesm.2604
@charlesm.2604 Год назад
@@leonardomangano6861 When you introduce dependency it goes down hill. Even if you only implement your interface in one class, it allows you to introduce loose-coupling in your codebase through Dependency Injection and Inversion of Control which make your project extendable and testable. In other words, when you start implementing new features, instead of coupling the actual implementation you can simply reference to its interface.
@nikogj9495
@nikogj9495 Год назад
This video made me recall a book from Sandi Metz "Practical Object Oriented Design in Ruby" where she wrote: "Code duplication is far cheaper (in terms of technical debt) than a wrong abstraction". Even if the book has been published years ago, I still find this sentence particulary true and relevant today. (I advice any curious programmer about OO to have a look at the book, even if you don't code in Ruby, that's my case, or even videos of her).
@cmdrsocks
@cmdrsocks Год назад
This is wise advice, code duplication is little more effort in most languages than Ctrl-C, Ctrl-V then change external names of the duplicated section. A well-designed abstraction on the other hand takes a deep knowledge of the problem domain and the intended end-user experience of the software. Often decisions about abstractions can only be made using feedback on the behaviour of the code in production. A premature abstraction will cost far more to unwind than the slight loss of efficiency caused by duplicate code. I design software for legal professionals at my current job and we are right now putting in some abstractions that have taken 2 years of use in production to figure out (i.e. will the time and money spent result in a meaningful improvement in workflow efficiency for users?) All textbooks ignore the two great limits in business: time and money. Outside the classroom you never have enough of either to do all the things, so often quick and dirty gets the product delivered to the paying customer and my paycheck delivered to my bank account. There is more inelegant dirty code that would fail any exam on software design out in the world than you will ever want to know about.
@awesomemix7777
@awesomemix7777 5 месяцев назад
most of the time I am like: "FU I don't care if it's repetitive (with some minor differences), I am not paid enough to write an error-proof abstract class"
@VitorMach
@VitorMach 3 месяца назад
One mistake I commonly see is people try to abstract away pieces of code that only coincidentally repeat, but that isn't inherently equivalent. This is closely related to the single responsibility principle and separation of concerns. Code might be similar, but if they have different reasons to change, they do not belong under the same abstraction.
@64BitLamp
@64BitLamp Год назад
In my opinion abstraction isn't necessarily about code reusability. I would argue it's really about shared generics. The big trope that most programmers never learn is abstract to interface coupling. What I mean is defining an interface for an abstraction, and using interface methods within implemented abstract classes. By defining an interface for abstract classes, you can call upon non existent implementations (that the implementor later has to make). Think of it more as "hot swapping black box functionally".
@gloweye
@gloweye Год назад
The big trope is actually that people abstract far too much. Plenty new programmers hear a couple buzzwords and go ball-deep into making interfaces for every little thing, even if there's no good reason to believe it needs it.
@64BitLamp
@64BitLamp Год назад
@@gloweye I remember in college they enforce that every class needs like 5+ interfaces. I agree there is no need for this but saying abstraction should be avoided is kind of ignorant. I've worked on codebases that have lived for 20+ years with both scenarios and I can tell you too many are far easier than too few.
@humanrye8696
@humanrye8696 Год назад
And this by itself takes testability to whole another level. Using example from the video: to test your high level functions you do not need to actually save the file. All you need to make sure (i.e. using mocks) that correct "saver" call was executed.
@muhammadharis2205
@muhammadharis2205 Год назад
Fuck this interface and class stuff.
@animanaut
@animanaut Год назад
@@humanrye8696 exactly, fully agree. having proper interfaces almost eliminates the need for some fancy mocking frameworks that do some voodoo like shit (reflections and stuff). if you have an interface you can simply program your own mocks by impementing the interface.
@ShilohFox
@ShilohFox Год назад
In “bad” abstraction like you had shown with the Save classes, coupling can become a problem. The purpose of abstraction is to remove coupling where there could be quicker, easier ways to process and store data as well as provide ways to represent concepts in code. The byproduct of abstraction is removing repetition in data structures. In a case where you’re holding multiple blobs of data, and they all have similar (but not the same) shapes, and the way you interact with them is all the same, abstraction can actually remove coupling by making the handling of that data independent of what the data under the hood actually is. Interfaces are a good example of abstraction that does not care for what kind of data you are handling. In Java, if you want to get a list of objects, then using the abstracted “List” as a return type versus specifying an ArrayList or LinkedList makes the handling implementation agnostic to the underlying implementation, and making changes under the hood does not affect how the code is written in the handling of that data.
@ultimatesoup
@ultimatesoup 9 месяцев назад
Yep, abstractions inherently remove couple contrary to what this video says. The video just has bad and poorly thought out abstractions which is why there is coupling. I would have a save interface that takes no parameters. Other classes implement it and any parameters they need to actually do the save are passed in at construction to the subclass constructors. All coupling is removed
@freedom13245
@freedom13245 Год назад
3:53 is a good example of polymorphism, a good abstraction that’s worth it would be a Save interface which both classes implement with their different way of saving, and you’d call the save method on the interface so you don’t have to add and “if” statement check the type and using a different implementation for each type
@SamuelLopez-mr5br
@SamuelLopez-mr5br Год назад
I think it's a good approach, but how will you handle the exception without violating Liskov subsitution principle ? Personally, i think the code smells overall... What I would do is create interface let's call it Saver, then use your GameConfig to store your Saver (new GameConfig() { saver = new SaveXML() }), finally all you have to do in that save method shown in the example is fetch the saver and call some save method with the state and name of the file. This removes the need for an exception to handle invalid save format.
@lachlanstanding7386
@lachlanstanding7386 Год назад
@@SamuelLopez-mr5br handle what exception? Your XML saving class shouldn't be throwing XML specific errors back to the consumer of your GenericSaverInterface. It should be throwing a GenericICouldntDoIt exception. If your XML saver class can't handle your XML error, what makes you think your GameObject is going to fair any better? Problem solved, Liskov satifisfied.
@RitchieDiamond
@RitchieDiamond Год назад
@@SamuelLopez-mr5br Catch the specialized errors inside each class and reraise them as an error common to both
@diskpoppy
@diskpoppy Год назад
yeah, "if" (and "switch") statements scary!
@minhan4444
@minhan4444 Год назад
@@diskpoppyif you want to avoid if, switch statements, then you can think of strategy pattern or factory method for solving it ❤
@AlanD20
@AlanD20 Год назад
I want to say, I agree small codes don't need to be abstracted to interfaces and many inherited classes, such as a todo app don't need 10 interfaces with 20 classes just to achieve the goal. Most people do abstractions because it makes the code maintainable and easier to read with consistent naming conventions. Let's say in your example that you want to add saving as JSON. When someone decides to add this class. First off, you think, what if we need to add another file mode? such as CSV? pdf? HTML? or any other file modes. When you work in a team of people, and each of those implementations has to be done by other people, You are going to use an interface or a parent class, that has all the functionality, and the one who implements the new mode knows what the new file mode must implement and where to implement their code without breaking any changes. Everyone on the team had agreed that the interface has to have a function that returns the file name, another function to save the file, and so on... In summary, it's more about maintainability and working on one large codebase with more than 3 people. Because it will be hard to make communication as someone might use saveAsJSON, someone else might use save_as_json, and someone else goes for saveJsonFile. There will be no consistency and an interface gives the developer a contract to follow those rules without breaking any other codes with no communication, other developers understand how the new file mode must have a function name called save which returns in different file modes. You have to know when and where not to abstract, everything doesn't need abstraction but on a large scale, you have to abstract. Otherwise, you will make the entire codebase a place where no one likes to debug nor to add new functionality because there are so many duplications and you don't even know if those are really duplicate or if they may do a different task but with the exact same process that looks duplicated.
@rcnhsuailsnyfiue2
@rcnhsuailsnyfiue2 Год назад
Great comment. Interfaces also make unit testing much easier, some frameworks will automatically resolve mocked implementations of things like filesystem drivers to ensure safety.
@AlanD20
@AlanD20 Год назад
​@@rcnhsuailsnyfiue2 Agreed. Interfaces are very useful when you start implementing Service or Repository patterns in your project. Overall, it makes complex projects that require a lot of maintainabilities much easier to handle in the long term. One thing I forgot to talk about is that most applications nowadays are very complex and aren't meant to be deployed once and for all. One of the most important parts of current application development is maintainability, and how can you grow the application over time without wasting time on redundant complex logic that could've been avoided at the start with better project architecture. Also, another crucial and significant parts of any project's development is the extendability of prior features without breaking them.
@SkyLee91
@SkyLee91 Год назад
Yes abstraction or interface, will help you to maintain the code in future. Yes it will be coupling but it is what we want right? If you get the handover source code from someone and you want to fix a bug that happened at Saving, you will look at FileSaver base class and see which are the child class, analysis the impact of changes. If your design s individual class as shown in the video, you need to change each of the individual classes' method one by one, which is what we saying DONT REPEAT YOURSELVES.
@wydx120
@wydx120 Год назад
You could use a wrapper that transparently chooses between the different savers, you know. No need to create a skeleton abstract class without any meaningful shared logic
@AlanD20
@AlanD20 Год назад
@@wydx120 You missed the point. I explained why there will be a need to create a skeleton abstract class.
@maybepumpkins
@maybepumpkins 5 месяцев назад
The "Rule of Three" is a good counterweight to "Don't Repeat Yourself" The idea is that you need at least three examples before you can decide what a good abstraction would be. If you try to write an abstraction too soon, you might abstract around concepts which turn out to be not very useful.
@super0spore0fan
@super0spore0fan Год назад
I think the major problem is that it seems the code wrote on the video favors inheritance over composition. Had the FileSaver class follow a bridge or strategy pattern, with the expected interface of the implementation component being something like "Save(fileName)", It would have worked better. Then, SaveToJSON would be one possible implementation, and the user would use FileSaver directly with either a default implementation or one of his choosing from the library we created. With dependency injection, he could create his own implementation to save files in any format he desires
@aoeu256
@aoeu256 11 месяцев назад
Java supports higher-order functions now, maybe try using them although you will have need pass through for arguments.
@chrishamilton1728
@chrishamilton1728 Год назад
I think you're talking about inheritance more than abstraction. Interfaces or abstract classes are a way to hide implementation details and actually decrease coupling. Inheritance / extending classes is what increases coupling.
@vikingthedude
@vikingthedude Год назад
I love this style of video where you walk us through various ways the code could be written and weigh their pros and cons. So many teachers either show only contrived examples, or just one big "correct" implementation. But your exploratory way of teaching is so helpful. Please keep making these in this style!
@erlend1587
@erlend1587 Год назад
Good video, but there are other ways of abstraction then inheritance, you could use composition, and create a component based system. You also don't need to make classes for everything, you could just make a function to handle some specific parsing. Not thinking too much ahead and being pragmatic about the code you write (keep it simple), often makes the code easier to reason about and later refactor when it changes.
@user-bk4gx9zh5v
@user-bk4gx9zh5v Год назад
Also thought about composition. (?)Something like StateSaver(stateSerializer).save(filePath, gameState)
@muadrico
@muadrico Год назад
Exactly. Of course, choosing the wrong abstraction will lead to code that is worse.
@crownstupid
@crownstupid Год назад
This is 100% right. The terrible thing that inheritance does to your mind is that it makes you want to cram everything into the same hierarchy of "things". (i.e. "File Savers") In this example, all the little pieces like the "periodic" file saver and the different file formats, and even the "streams" that the bytes are sent to from the conversion should all be interfaces that are composed together.
@sir_no_name1478
@sir_no_name1478 Год назад
Searched for this comment
@iantaakalla8180
@iantaakalla8180 Год назад
Then what is inheritance good for? Inheritance is talked about but then every other instance of inheritance seems like it’s poorly-thought-out interfaces or something akin to that.
@tonnoz
@tonnoz Год назад
I usually try to avoid inheritance as much as possible and use composition instead. By embedding functionality you can still retain the meaning of "shared functionality" while separating the differences in separate classes. Of course only if it's worth it. The code you shown are a good example (more than two choices etc.).
@JamesPhipps
@JamesPhipps Год назад
This. Inheritance is one small part of OOP programming that somehow became the dominant aspect instead of polymorphism, composition, and encapsulation which were the focus of its original advocates. I’m not a big Uncle Bob guy but he’s dead on about this.
@cryp0g00n4
@cryp0g00n4 Год назад
There is nothing wrong with inheritance. Even a composition maybe a type of base class with a standard interface. The key is to decompose and organize functionality at a data/protocol level... that is work with properly describing the data and then focus on the protocol (i.e., abstractions etc). Just think OSI model... perfect example of proper design principles. All design principles come from the desire to elegantly design protocols.
@aoeu256
@aoeu256 11 месяцев назад
@@JamesPhipps You can have "polymorphism" in Python, JS,Ruby,Lisps(Clojure), Smalltalk by just passing in functions and passing arguments through, ne need to use inheritance, classes, or even even interfaces. def save(saveFunc=Save.XML, *args)
@jayocaine2946
@jayocaine2946 6 месяцев назад
@@cryp0g00n4 there is everything wrong with inheritance if you're writing a shader, and in a lot of cases with systems programming, hell even game programming inheritance adds extra bloat. Like anything else in life and programming, it all depends on the situation and talking about some general idea of "inheritance" and "abstraction" is really quite useless and only intermediate devs do it
@ttcc5273
@ttcc5273 4 месяца назад
I once wrote a file parsing toolkit that relied heavily on inheritance. It was super powerful and you could parse new formats with just a few lines of code. The only problem was that I was the only developer who knew the hierarchy well enough to write those few new lines. Lesson learned: inheritance is great when you are the one designing and using the thing… but it leads to systems that require a big cognitive burden to approach from the outside-in. Composition has proven to be more useful and other-developer-friendly. Subunits are easier to mentally visualize than graphs of inherited functions. A flat library of functions is easier to use than a vertical tree of functions that have nuanced differences depending where the caller lives in the hierarchy.
@gr0nis
@gr0nis Год назад
I would suggest using composition over inheritance and also use more functional style programming. It’s much easier to have abstraction that way without locking yourself too much into coupling imo.
@davidmartensson273
@davidmartensson273 Год назад
All have their merits and knowing more ways can make the code better. Fully functional is only really worth if with a language/compiler that can use the fact that the code is functional to optimize the compilation. If your using a more traditional language, functional constructs can still help improve code but some of it can also be much less performant since real functional languages use many hidden optimizations that a non functional language cannot apply.
@aoeu256
@aoeu256 11 месяцев назад
I was thinking doing it like this in Python/Ruby/JS (simple dependency injection?). def save(saveFunc=Save.XML, *args, **kwargs): ....# do some stuff with args and kwargs ....result = saveFunc(*args, **kwargs) # pass all other arguments straight to this func ....return somefunc(result) , the save method would do a bunch of stuff then call your saveFunc using keyword arguments (which are objects) that are automatically passed to your saveFunc. This reduces coupling, but you need intergration tests to make sure code is exercised but your program will probably have errors anyway if you don't do testing, I believe this is "message oriented" OOP programming.
@thelatestartosrs
@thelatestartosrs Год назад
The base class for the filename does not create coupling to a filename, that was already there, if you didn't want the saver to save to a file its not the base class' fault. If anything, if when trying to abstract you think it creates a problem it means you already had a problem before. in case anyone didn't get it the problem is that XML has little to do with the file, in other words a saver needs a saving strategy like file or db and that strategy needs an optional format. say `new Saver(new DBSavingStrategy(db_handle))` or `new Saver(new FileSavingStrategy(file_handle, file_format))`.
@ktvx.94
@ktvx.94 Год назад
There're times when coupling is good. Sometimes you _want_ to update something that changes all children classes simultaneously, instead of keeping track of which ones need the new code and manually changing it every time. If you forget, different entities that should have the same behavior start behaving differently. That said, I fully agree with everything else.
@foible2085
@foible2085 Год назад
One consideration might be the skill level of other developers on your team. Delaying abstraction only until the benefits outweigh the coupling might mean you will encounter a huge mess the next time you need to work in this area of the code due to all the cumulative changes from the team. Setting up the abstraction early will encourage better design for future changes even if it may seem over engineered in the interim.
@Bluedragon2513
@Bluedragon2513 Год назад
For instance, in a game, I might have to implement a global EXP multiplier for certain days of the week. Rather than having a variable be directly accessed in 60 different functions, I should just abstract the EXP variable to be handled by a class, allowing a global EXP multiplier to be more easily maintained within that single class. I think?
@matthewvandenheuvel8401
@matthewvandenheuvel8401 Год назад
For some projects a senior dev who was very used to working with architectures used to lay the basics, he would then relay it to me for the implementation. I'd always be lost at first but I always ended up understanding the reasoning later, and the motivation too as some other projects were deeply ingrained in that whole coupling mess. So I'd totally agree with that, it's better to conceptualize and plan early so that you don't end up building on a mess
@SamuelLopez-mr5br
@SamuelLopez-mr5br Год назад
yes abstraction can be great tool for design especially if paired with TDD.
@matias-eduardo
@matias-eduardo Год назад
It's funny because most beginner code is decent code. It's when you start getting "mid-to-high" skilled developers that things go bad. It takes time to "unlearn" all of those "best practices". People tend to interpret complexity as sophistication.
@rangedfighter
@rangedfighter Год назад
@@matias-eduardo Well, in a programming class or when learning most of the time you work alone. Once you have a bunch of full stack programmers, each of which with different favorite/main languages and paradigms or simply different ways of thinking, that stuff goes south more than 0% of the time. Or when you have too much "democracy" and 20 different decisions that don't all fit together get made. So you have no coherent design of the whole programm, but a mess of concepts.
@keokawasaki7833
@keokawasaki7833 Год назад
Don't abstract away code, abstract away the idea
@LordErnie
@LordErnie 4 месяца назад
To be honest, good abstractions aren't made because you want to limit duplicate code. Sometimes two processes, even though they are completely unrelated, do things the same way. That doesn't mean that they should share an abstraction. Sometimes things just look alike. The point of abstraction has never been, and will never be, limiting code duplication. It is all about sharing contextual abstractions. Hey I need to read an XML file to this format. That is translated to wanting to read a file that is in a format, and translating it to specific type X. That is an abstraction. Abstracting an account and a vault because they both have passswords isn't. Sometimes two or more things just look alike, but they aren't the same, even though they do things in the same way. Never abstract to limit duplication, abstract because you want to introduce flexibility to your system.
@mcmillanator
@mcmillanator Год назад
It's so easy to get this wrong and can be difficult to teach to someone else. You've done a great job at explaining it clearly and simply. Thank you.
@not_vinkami
@not_vinkami Год назад
As a math student and a programmer, I'd say doing abstractions is like doing factorization When you see a few terms, like x²+2x, you can factor it into x(x+2), which makes some further actions simpler because you have separated components to work with. However, this isn't always the case to do. For example, x²+1 isn't that good to be factored to (x+i)(x-i). This only made your original polynomial messier. This is so similar to code abstraction in the way that, you have to identify what's worth abstracting, and how it can help in further applications. Doing abstractions in already very simplified code only gives coupling, just like the x²+1 case I've mentioned. You can just call it not factorable (not abstractable) and peace out.
@mrsharpie7899
@mrsharpie7899 Год назад
Small correction: it's (x^2)-1 that gets factored into (x+1)(x-1)
@not_vinkami
@not_vinkami Год назад
@@mrsharpie7899 what are you even correcting?
@JakoTheWacko
@JakoTheWacko Год назад
@@not_vinkami I believe they’re unaware that x^2 + 1 = (x-i)(x+i) and they’d assumed you’d meant x^2 -1 = (x-1)(x+1)
@iantaakalla8180
@iantaakalla8180 Год назад
To be fair, when you really want to emphasize the imaginary answers of x^2 + 1, that factorization is useful.
@trollnat9857
@trollnat9857 Год назад
As a physicist, sometimes (x+i)(x-i) is a useful factorization for (x^2)+1, but I agree that in most cases you're better off not factoring into imaginary components. And I think that's very apt for abstraction, even edge case abstraction can be useful, but outside of the edge case you designed it for the usefulness does not justify it.
@thommekm
@thommekm Год назад
I just wanted to say that I really like your presentation style - working with examples from real use cases and showing how the code changes. That is very helpful and easy to follow, good job!
@yrds96
@yrds96 Год назад
You know this channel is good when it's 11 days old and already have 17k subs with only 2 videos.
@lachlanstanding7386
@lachlanstanding7386 Год назад
this video is only useful as an example of the kind of logical circles you can run around in if you don't understand SOLID principles. Please don't follow the advice in this video.
@ecm9251
@ecm9251 Год назад
@@yrds96 That's not good, that's popular.
@klikkolee
@klikkolee Год назад
I think separating the ideas of abstraction and repetition helps a lot to choose good abstractions. Abstractions are supposed to guide your field of view to whatever is relevant to a given part of code. If one part of your code needs a means of saving a game, but doesn't need to know how it's done, then an abstraction for saving a game is beneficial. From the perspective of what code changes guarantee the need for other changes as a matter of being able to compile the code, an abstraction will always create coupling. But if the abstraction was a good choice, then from the perspective of what code changes are required for the code to continue being sensible, that coupling already existed. The abstraction just enforces it. A good choice of abstraction can also reduce coupling of both kinds from other parts of the code -- a Saver interface means that code that uses Savers can have just one variable and from its perspective one code path regardless of how many types of Saver exist. Lastly, a good choice of abstraction pushes coupling into islands of relevance where you can readily see what depends on what and why. Code that just needs a Saver and doesn't care which Saver still needs to get a Saver from somewhere, and that somewhere needs to contend with the different reasons one might be chosen over another and needs to contend with the needs of creating individual savers. That is coupling, but it's coupling that would've been diffused through the code base without the abstraction. I am struggling to make sense of your Saver example. I just cannot think of a situation where I would have multiple methods of saving something but would not have a part of the code that shouldn't care which method is employed. That is a situation that is impractical to accommodation without an abstraction. This situation also means the example of wanting to change the interface of just one does not make sense. If the code using savers does not need these additional features, then adding those features to any savers is unused code. If the code using savers does need these additional features, it probably needs them regardless of which kind of saver it is told to use, meaning that it needs to be added to all of the savers -- regardless of whether there is an actual in-language interface that enforces the change. I don't choose what I save and how I save based on the serialization format -- I choose the serialization format based on what I save and how I save. If a given serialization format or some other aspect of a saver isn't compatible with the new needs of the rest of the code, that saver is getting the boot.
@codingbloke
@codingbloke Год назад
Nice one! Completely agree with this! There is a difference between "repeating the same logic" and "by coincidence having the same logic currently". Spotting the difference can be hard but if you eliminate the apparent repetition of the latter you can find the code becoming less malleable . It can perversely be a source of bugs. Class A shares some logic with Class B. Class A doesn't behave correctly because there is a "bug" in the shared logic, however this "bug" is only truly a bug from the point of view of Class A, for class B it is perfectly correct. So you fix the bug, now you have broken Class B and its hard to know you have done that unless you check also Class B (and C, D...). Even with strong automated testing that newly introduced bug in Class B may go unnoticed. Also when shared logic is no longer compatible with all consumers there is a tendency for ugly `bool` values to appear in parameter lists and the code becomes harder to understand and even more bug prone.
@mmaldonadojr
@mmaldonadojr Год назад
As I mainly write firmware for microcontrollers in C, my main criterion for abstracting or not a piece of code is whether this will save on memory. Maintainability is then a close second (may be prioritized over memory if the amount of memory saved otherwise is small). Elegance that's not essential to maintainability comes third. Embedded programming is quite a fun field!
@meatbleed
@meatbleed Год назад
that's awesome
@aoeu256
@aoeu256 11 месяцев назад
Instead of coding in C, why not code in Rust or LISP and use Lisp macros as a super-powerful compiler generator... Also there is an option to inline functions or not, and often not inlining functions can let the code fit in cache.
@jayocaine2946
@jayocaine2946 6 месяцев назад
@@aoeu256 because why would you do that? lol, C is the king
@AnyVideo999
@AnyVideo999 Год назад
This is the situation at my work right now. Crazy balance between coupling and abstraction to deal with custom requirements growing. Every vendor has a different workflow and customers all want very unique things. It was once very coupled on the vendor side but we've broken it up a bit now.
@ciberman
@ciberman Год назад
I REALLY love this way of showing code. Explaining what are you doing while showing a speed up video of the changes.
@alexmercerind
@alexmercerind Год назад
I'm so glad I discovered this channel. I think I'm early, watched the two videos. I'd definitely love to see code architecture & structuring related videos.
@Beeftitan
@Beeftitan Год назад
The value in not repeating code is for the sake of maintainability. If you fix a bit of code that is shared across multiple functions you can be prone to miss all the spots that need updating. I typically dont let functions do more than "1 thing". I also am not doing OOP anymore. When a function does "1 thing" then its easy to generalize functions that can be generalized and functions that need to be hyper specific can be hyper specific
@ArnabAnimeshDas
@ArnabAnimeshDas Год назад
If I have multiple functions I place them in one separate file.
@Yupppi
@Yupppi Год назад
What do you think about abstraction in the terms of readability? What I mean is when the naming is on point (on top of your logic in abstracting things), you can read the "headlines" of the code and it divides into very understandable blocks and chunks and functionalities for human brain, and you quickly get a grasp of the big picture. With a lot of repetitive coding you're a bit in the jungle trying to see the forest from the undergrowth, you're whacking with a machete left and right trying to piece together the general picture of repetitive sections, you're creating the library of structure in your head.
@jacobkirstein4798
@jacobkirstein4798 Год назад
I think an often overlooked benefit of abstraction is that, if done properly, it makes understanding code simpler.
@JamesPhipps
@JamesPhipps Год назад
Yup. If you’re in a large org writing shared libraries or frameworks and you don’t understand abstraction you’re not going to have a job for very long. If you’re on a small team and insist on “pre-factoring” so much I need to have six files open just to figure out how I’m going to handle the response from an API endpoint let alone update models/UI then you’re not gonna have any friends.
@ZahrDalsk
@ZahrDalsk Год назад
However, his examples use inheritance, which makes understanding code needlessly harder. Never ever EVER use inheritance if you can avoid it, use composition instead!
@ucantSQ
@ucantSQ Год назад
I just watched you videos on naming variables and nesting code. I ran over to see what other videos you had and realized how new this channel is. Keep it up! This will be super useful to anyone new to programming. You bring out subtleties and nuances in coding that I myself have struggled with, but never given voice to. It's reassuring to hear that repeating code is ok, because in some cases it feels much more natural to do so, but I tend to feel some bizzare responsiblity to generalize when possible. Even though I'm writing code solo, for myself.
@zeckul
@zeckul Год назад
Abstraction *through inheritance* creates coupling. Once you mostly let go of class hierarchies and choose to program using data types and functions instead, most of these issues go away by themselves.
@astronemir
@astronemir Год назад
I usually find that good abstraction reduces coupling. I always aim to reduce coupling in my refactors and in my design. Having that as a goal works way better than anything else.
@Imevul
@Imevul Год назад
The problem in the example is really that the different savers have too much responsibility, namely both serializing the game state to a certain format, and also writing it to some kind of storage. This would make much more sense if you had an XMLSerializer and JSONSerializer that only did just that: take some input data and convert it to the correct format, then return it either as a string or a stream. Then you would have a GameManager that uses one type of serializer (DI) to facilitate the whole logic of "saveGame", and one or more storage connection classes (also DI), for example. In the end, you would end up with two interfaces: Serializer, Storage And several single purpose classes: GameManager, XMLSerializer, JSONSerializer, FileStorage, SqliteStorage, etc.
@natenez
@natenez Год назад
Completely agree. And maybe use the strategy pattern if the serializers need separation between. ‘Walk the data structure’ and “emit the serialized blobs” code
@chrismingay6005
@chrismingay6005 Год назад
Great video that puts into words things I've felt for a while! I'm working on a project thats heavily abstracted on multiple levels. While there is less code overall it's very difficult to pick up, you get no sense of flow and it's a pain to debug. It feels like overly abstracted code makes it more and more difficult to stray from the beaten track and eventually you will have to.
@RyanBrockey
@RyanBrockey Год назад
This channel is great. I'm a hobby programmer and I find each of your videos very instructive and encouraging. Thank you. I'm looking forward to your next video!
@kunodragon4355
@kunodragon4355 Год назад
I've never run into any system that suffers from overcoupling. Instead, I've often had to deal with systems that have copypasta code galore, dozens upon dozens of lines, and then when a feature needs changing, and it needs to happen everywhere, you gotta go and find all instances of this code and modify it X times, and test each one, because sometimes they have subtle differences and...
@RCL89
@RCL89 Год назад
I feel you. Too much repetition hides the subtle differences and buries the underlying structure of the code - its business logic. Even if you only need to change one place, understanding the code and finding the right place to change is hard. Without a good structured/abstracted code, e.g. with everything inside one big KLOC function, you don't find what blocks are relevant for you or which ones to ignore, because it's too much to parse by your brain at once.
@riahmatic
@riahmatic Год назад
"identifying repetition and extracting it out" is honestly a junior/mid level understanding of abstraction. What you've described are the dangers of inheritance. The real power of abstraction is realized when you starting thinking about interfaces, layers, and domains. E.g. separation of concerns is achieved through abstraction ,and that decreases coupling.
@yassay5854
@yassay5854 Год назад
Good looking Channel, short format, single subject, calm ton & relaxing background music, visual/ easy to understand/synthetic examples & explanations, this is probably gonna be a killer channel, glad to be here at the begining of the story, whish you the best :)
@jeromesimms
@jeromesimms Год назад
This is so insightful, it really brings together what I have been learning in my Software Design and Development course. I subbed and would like to see more videos like this
@insomnia20422
@insomnia20422 Год назад
Could also be the other way around that due to poor abstraction you need to change code in a lot of places. I think the coupling thing is mostly an issue if you have poorly designed code that needs to be reworked. So the solution is, dont design your code poorly.
@YuriG03042
@YuriG03042 Год назад
so what you are saying is: skill issue
@eidiazcas
@eidiazcas Год назад
Agree
@tobiasbergkvist4520
@tobiasbergkvist4520 Год назад
Except you typically don't know where you want your coupling to be, and where you don't want it ahead of time. So start with repetition first, and then create abstractions later once you notice you want to change things at the same time.
@rutabega306
@rutabega306 Год назад
Yep, I think it is good to ask "Is coupling desired here?", and often the answer is "yes".
@_Aarius_
@_Aarius_ Год назад
Interefaces are also important to consider. in this videos examples, you could have as many diofferent types of savers as you want, with one bit of logic to determine which to use, that all share a common interface, and then set up a way to check if its using interval at runtime and set the hook for that, for example. But, interface design is tricky, and you can end up with extra coupling where a certain method doesn't make sense for a particular implementation...
@karolborecki9003
@karolborecki9003 Год назад
I don't think the example code showed shows that abstraction causes coupling. It shows that poorly written abstraction causes coupling. In the case of a file saver You can create abstract class FileSaver that holds not the filename but another abstract class DataStream, which resolves coupling problem
@kaiserouo
@kaiserouo Год назад
This video decribes me toooo well. After learning OOP and design pattern stuff I am always thinking about "how" I can abstract something, not "when". And that indeed screw things up a lot later.
@cal-overflow
@cal-overflow Год назад
Great video. I think we as developers need to often stop and ask ourselves, "am I over-engineering this by implementing it this way?" Instead of just assuming they're doing the best work because it's a common practice. Otherwise it often causes immense headache when another developer (or even the person who designed something) need to refactor or change something. Thank you for bringing attention to this overlooked topic!
@creo_one
@creo_one Год назад
Cant wait until he discovers Dependency Injection and Interfaces, then learn to use it properly... until then, he will rediscover whole OOP piece by piece
@TheMyHalo
@TheMyHalo Год назад
I just found your channel and love both videos. Really hope youre going on with this style
@jonan.gueorguiev
@jonan.gueorguiev Год назад
Great approach for the craft of programming! I really like the incorporation of the word "aesthetic" - I've found it myself that beautifully working code _is_ better. So, on the topic of abstraction - a broader generalization of what you've shown us, is that - if an abstraction is created to capture common ground from inside - implementation, tools, libraries, etc - it is usually unnecessary coupling. On the other hand - when abstraction tries to capture common view from outside - i.e. from those who _use_ the code, then it is usually OK, because it actually introduces a generalization, a true abstraction, and not just a code-duplication-removal.
@ZachHixsonTutorials
@ZachHixsonTutorials Год назад
What you're referring to here as abstraction seems to mostly just be inheritance. Abstraction deals more with making sense of raw data. EX: instead of remembering 3 numbers and an image, we group those together and call them a "GameObject," since it's easier to think about that way. Instead of calling 5 seemingly unrelated network functions, we wrap those up in an "UpdateServerState()," function because it makes more sense to read.
@Outfrost
@Outfrost Год назад
In oop terms, abstraction _is_ inheritance (and traits/interfaces, if they're separate in your language). Sure, we can call any struct or collection an "abstraction layer", but it doesn't help us reason about code architecture
@ZachHixsonTutorials
@ZachHixsonTutorials Год назад
@@Outfrost Reasoning about code architecture is literally what abstraction is. Abstraction is in every language. It's the act of naming and structuring things to give context to the code architecture in a way that "abstracts," the mental model away from the implementation, thus implementing a layer of reason into the system. In the context of OOP abstraction is sometimes defined as the act of exposing/hiding properties and methods within a class, which still has nothing to do with inheritance.
@Outfrost
@Outfrost Год назад
@@ZachHixsonTutorials ok purist
@natenez
@natenez Год назад
While inheritance is one form of abstraction, others forms exist. A DateTime class is an abstraction, regardless if it doesn’t inherit from anything. Private methods, with good names of course, can (but not always) be abstraction. Really the title of the video should be something like Bad Class Hierarchy Design Makes Your Code Worse. Which of course is true. And given there are lots more ways to design a bad hierarchy than good one, most of the ones developers deal with are closer to the bad end than good one.
@yuryzhuravlev2312
@yuryzhuravlev2312 Год назад
If you start using langauges with optional classes (python, C++) or without classes (go, rust) all this becoming even more interesting! Basicaly, in most cases you shouldn't make abstractions at all, it's needed in rare cases. Procedure decomposition much better in many common cases, and OOP (especial with classes) can help in some corner cases only.
@AnotherFancyUser
@AnotherFancyUser Год назад
First and foremost, I recently saw your videos, I love how you make them, I like the aesthetics of the edits and I like how you educate about these concepts, so kudos to you, I'm subscribed. Now, to me abstraction means more than just create classes and hide implementation details. Abstraction to me also means the usage of interfaces and the elimination of 'new' which ties your classes together (Tight coupling). Your FileSaver.Create method is still coupled to concretions like AWSSAver, SaveXML, etc. You could make the FileSaver where T is an IFile or a representation of something needed to save, that type T knows how to save itself because the "can do" contract is about implementing the Save method, after that you have different approaches, you can either use a factory pattern to make the concretion or better yet use a container that contains all the types something like services.Add() etc, and you can even change the abstraction on runtime, after that FileSaver can be much more, you can have an IEnumerable of files to save them, you can create a queue that saves them following a CRON or saves them using hangfire, etc, so you can use composition and have this IEnumerable as a property to do whatever you need. To me, the idea of abstraction should be there to talk about them as it is things in the real world (concepts or physical objects), if you can say them speaking using language then you can do it properly. Now, one rule of thumb I would use is... don't start with abstractions, start with the code all over a single piece of method and from there start the refactoring process, no duplicate code, separate things into methods, group behaviors and reasons to change into classes, etc., I don't think starting abstracting is a good idea, the main reason is because an implementation can change in the future because that Jira or whatever feature you are creating can change tomorrow.
@exploringgodscountry
@exploringgodscountry Месяц назад
I need to hug you! It's INSANE how much abstraction is going on. I am trying to learn Android OS API / programming. The examples I find are abstracting interfaces already designed in the API. Obfuscating the underlying "Source of Truth" in the logic. Programmers seem to have fallen so much in love with coding their own classes that they code data types over the top of datatypes just for the sake of loving their own coding style and not much more thought. Case in point, 20 years ago graduated as a computer engineer. Code was closer to hardware level. Now that's all abstracted. It's good we don't have to code the hardware interfaces but it's to the point the code for interfacing with hardware is MUCH more complicated than the hardware itself. Have we really gained anything in that process? I used to look at a datasheet and then write the code. What I needed was right in front of me to write my program. Now you interface with code to request access to hardware from the OS. In many instances that interfaces / code is scarcely documented... even if it is well documented you run into unknown dependencies and some degree of uncertinty in how the code interacts with the hardware. Many times there's tens of pages for a software interface (sometimes 100+ pages of documentation), when I used to deal with only a few page datasheet on the hardware resource. I don't think we've really made much progress to be honest with all this abstraction. There is definitely a balancing point and I feel we've far exceeded it to the point of too much code.
@mexico14000
@mexico14000 Год назад
Been debating about abstraction in my both my React and API code. On one half, I enjoy knowing how each function's flow. On the other, I like writing less. I haven't really touched OOP recently, but I'm trying to avoid it as functional programming is something I prefer working on in the future.
@DanDanDandelion
@DanDanDandelion Год назад
Well with higher order functions you are welcome back to the world of abstraction :)
@mexico14000
@mexico14000 Год назад
@@DanDanDandelion Yeah but it's less code regardless. I worked with functional programming on Scala and rarely passed 50+ lines of code for basic college assignments.
@aleixlopez5600
@aleixlopez5600 Год назад
Just found your channel, you've a new subscriber, really like the dedication to some topics related to clean code. Could you do a video about your take in OOP, second level inheritance, MVC and how what's been considered good practices has been changing through the years ?
@tally3018
@tally3018 Год назад
Just found ur channel. Looks like ur doing absolutely great after only 2 weeks uptime! Subscribed!
@animanaut
@animanaut Год назад
good video. one question you can always ask yourself is: what is the blast radius of this change. having code duplication can be a good thing if you want to reduce the blast radius. "do i want to break y when i fix this for x". sometimes, instead of abstracting something away to a library, just leave it where it is until you really need to abstract it. "YAGNI" can be perfectly applied to pre mature abstractions ("you aint gonna need it")
@TheDesttinghim
@TheDesttinghim Год назад
Thinking about the data you have and what you need to do to it is more important than figuring out abstractions. Figure out what data you actually need to perform the work and write code to operate on that struct. This is the main idea behind data oriented design
@JordanDavidson3102
@JordanDavidson3102 Год назад
For me it's all about defining abstractions that couple the code in a useful way. In other words if there's code that when it changes, by definition (not by convenience) must change in all places then it should be coupled and therefore abstracted.
@matias-eduardo
@matias-eduardo Год назад
I agree. One way to think about it is that you're moving the coupling/complexity (or, more concretely, the things that need to happen and the order in which they need to happen to transform the data) to an isolated place (for example, a single enum) so that the rest of your code doesn't need to "think" about those things. It just needs to "do the thing". This is also why programming languages with proper reflection are so powerful, they can enforce things to happen in your code at compile-time so that the implementation itself does not need to "think" about it. To me this is what good de-coupling means. You move complexity to few places where you actually need to "think" about it, and the rest is just writing implementation code.
@friedrichwaterson3185
@friedrichwaterson3185 Год назад
Such an awesome thematic ! I hope your channel grows fast.
@tobyjacobs1310
@tobyjacobs1310 Год назад
I've definitely seen both scenarios, as well as the one where over-abstracted code losses all meaning. That said I've also had the experience where accession reduces coupling, because dependencies drop off. This is especially true when using DI.
@redlancer7263
@redlancer7263 Год назад
I would love a video about file system structure! I know its always going to be different between projects but I know there are common patters with /src, /docs, /bin, etc. Are there principles we should be following as to not get too nested or too shallow in our file structure?
@blueghost3649
@blueghost3649 Год назад
Just do what makes sense for your project, any convention for this would be dumb since every project has different needs
@TruthAndLoyalty
@TruthAndLoyalty Год назад
Ui rules" apply here. If you have navigation menus, it will take a long time to find what you're looking for if you have a lot of levels with a lot of generic split paths that end up hard to navigate. On the other hand, good luck finding a file in a completely flat directory for a sizable project. This is a version of "the hardest problem in programming is naming things". Only nest when you have a logical group with an accurate name and doing so would make it less difficult to navigate. Don't nest things just to "organize". Start with very broad understandable groups. Keep things wide unless there's a good reason not to.
@redlancer7263
@redlancer7263 Год назад
@@TruthAndLoyalty Awesome, that makes sense. I hate digging through pages and pages of game UI or inventory dividers but I like that all my weapons and armor are grouped. Thank you!
@benbertheau
@benbertheau Год назад
i just love your animation style very simple and stylish
@notreallyasloth
@notreallyasloth Год назад
Your videos are so good. Can’t wait to see what you do next ❤
@DynamicalisBlue
@DynamicalisBlue Год назад
It works well when you program in a way where you only need to know about the interface. Saving by interface makes it way easier to transition to another save format later down the line. If done right, you can change one line of code and now your entire code base is using a different file format.
@TheKimpula
@TheKimpula Год назад
Great video! I tend to agree with you. Love the presentation. Looking forward to what you're planning on creating next.
@AI-Effect
@AI-Effect Год назад
Code is an art and a state of mind. I think that abstraction can't be learned, some feel it and some never, even with years of development. This is what differentiates an excellent programmer from others, an author of widely used libraries, frameworks, languages from others. Very good visual quality of the video, can't wait to see where this channel will take us.
@katto1937
@katto1937 Год назад
Everything can be learned, nobody is born with knowledge or natural aptitude towards programming abstraction.
@02orochi
@02orochi Год назад
@@katto1937 agree w first, disagree w second some take longer to learn certain concepts than others
@5cover
@5cover Год назад
I disagree. There has to be an objective way to write better code, and avoid common pitfalls.
@katto1937
@katto1937 Год назад
@@02orochi Maybe at first, but over an entire career this will completely even out as you will be very slow at learning other things. And the huge majority you learn faster because you've already done something similar before. Search up the violin study on natural proficiency vs hard work.
@lachlanstanding7386
@lachlanstanding7386 Год назад
no, it's very much a science, with rules, that you can learn. This is just what people say when they haven't learned them yet.
@sandropollastrini2707
@sandropollastrini2707 Год назад
You just coupled in my mind all the abstractions I did in the past by this abstract framework of abstraction-coupling. I will abstract no more in the same way, being now coupled to your ideas.
@hvaghani
@hvaghani Год назад
Subscribed immediately! Btw what theme are you using? it looks very clean.
@RainbowVision
@RainbowVision Год назад
I disagree with the save example, because you want to provide a unified way of saving and loading your game to an arbitrary file format. The other components are not responsible for knowing how the game is saved, just that it's 'savable' (so interface)
@eidiazcas
@eidiazcas Год назад
Agree, the ugly abstraction was the problem, not abstractik itself, thats why we need composition ans dependency injection
@mody1710
@mody1710 Год назад
Cool stuff. I think one important thing to consider here is that abstraction doesn’t increase overall coupling per say. In your example, with no abstraction the coupling will now happen with the rest of your code interacting with your saver, and I don’t know if that’s a better scenario. Architecture flows so the whole breadth of how the system works matters. The goal is to lock your features for modification, so removing the XML saver for example won’t break logic in multiple other classes needlessly that were already tested. This is why the dependency rule is probably one of the most important rules to stick with. Your application should be split into layers, where there’s a saving top layer, for example. That layer should manage the bottom layers where you might have any variety of savers. That way, the rest of your code doesn’t care what happens when you save. It only knows to call a standard method on the top layer. That way, it truly doesn’t matter if you abstract your savers or not, because their lifecycle will be managed in a single place and any number of changes to the savers won’t result in changing code in unrelated features, which is often the way developers introduce bugs to existing functional features.
@aaronclement1266
@aaronclement1266 11 месяцев назад
This came at a good time for me. I'm rewriting my game engine's code and I have felt the effects of coupling without really understanding what I was doing wrong.
@sayaks12
@sayaks12 Год назад
another problem here is prevalent use of inheritance, since inheritance often couples things together much more tightly than a compositional approach. sometimes that can be the right thing to do, but oop definitely pushes it much more than is actually useful
@zyansheep
@zyansheep Год назад
If it was me and I wasn't limited by OOP abstractions, I would create an interface (trait) "Saver" that contains an associated type "Resource", a "constructor(Resource)" and a "save(GameState)" method. And then just pass a specific implementation of "Saver" to the Game object.
@jonohiggs
@jonohiggs Год назад
I think the missing piece is that inheritance is the only method of abstraction presented, and no mention of composition. GameObject is not great because it is typically implemented with inheritance but a better solution is ECS which is a compositional / data-oriented pattern, the same concepts are abstracted but in different ways
@zyansheep
@zyansheep Год назад
@@jonohiggs absolutely, OOP-style inheritance is an outdated form of abstraction...
@Whelknarge
@Whelknarge Год назад
I learned to code on the job over the years, I don't have an academic CS background, and I've often noticed benefits of the academic training some of my colleagues received that make me kind of wish I had done a CS degree. That said, I have also notice a tendency in them to follow certain rules and conventions blindly, having been taught that that is the "right" way to do it. Everything you've said in this video just seems like common sense to me, it wouldn't occur to me that I "must" always avoid every scrap of repetition as a rule - repeting code is bad not just because it's "wrong", it's bad because I have to maintain more code, so the more I'm repeating the worse it is. But obviously, having the code defined separately in each class means I can change one without affecting the others. So I do whatever I feel is the best cost-benefit solution for my case. If you understand why these things are good or bad, you don't need formalised rules for when you should or shouldn't do something because it's common sense.
@slephy
@slephy Год назад
Thanks for sharing this! Perhaps slightly off topic, but one thing I'm (quickly) starting to discover is that even though I may have cleaner, compact and more readable code, it may not necessarily run faster. Being specific and taking the time to type out exactly what I want to happen when I can be specific may take me more time to do, but it performs faster than, for instance, looping for a solution. (This is Mike Action's thesis in a nutshell really.)
@billj5645
@billj5645 Год назад
Go back in time- this is similar to (the same as?) using subroutines, functions, procedures. Whatever you call it, the smaller piece that you break out from the main code should do just one job, that way you don't have the coupling. If you need a procedure to do maybe 2 things, you break it into multiple procedures that each just do one thing. Now the coupling is gone.
@Zoomakroom
@Zoomakroom 5 месяцев назад
Abstraction doesn't increase coupling when used properly. Abstraction hides complex ideas behind simpler ones. There is nothing simpler about inheriting from a FileSaver class if the only thing you share is the filename. This poorly chosen example feels like too much of a straw-man and I think you're doing people a disservice by confusing an example of bad object-oriented design with the idea of abstraction, which has no inherent relation to OOP.
@aidenkim6629
@aidenkim6629 Год назад
The 3b1b of code :)
@encapsulatio
@encapsulatio Год назад
Not really. There are others I can think of that have even higher production animations.
@aidenkim6629
@aidenkim6629 Год назад
@@encapsulatio Even if the production isn’t the same quality as a channel like Reducible. I found his explanations to be straight forward and logical.
@danielwilms6919
@danielwilms6919 Год назад
Avoiding repetition is rule of thumb that should help us to make the code easier to maintain. If using a bit of duplicate code is in the end increasing the maintainablility, because we don't have to use abstraction, that is a good thing.
@jebwatson
@jebwatson Год назад
I really appreciate the thought that abstraction and coupling are opposing forces. I've never considered it from that perspective and I think this will help me when designing future systems.
@bjorn1761
@bjorn1761 Год назад
You got it wrong: you tried to implement 2 abstractions in 1. The first is what we call serialization. And the second just the transportation (file, network). The way to do the first is by using the Visitor Pattern. So you then have a json serializer and a xml serializer. The object to serialize should then implement the serializable interface (which can call save methods of the given serializer, that construct is called "double dispatch"). Use the Visitor Pattern in cases where 1: you have hierarchical data(structure) and 2: want to (depency) inject behaviour into the object.
@matthias916
@matthias916 Год назад
No offense, just curious, but do you have any experience with writing code for actual companies?
@kpbmx
@kpbmx Год назад
This is the video that needs making
@georgeokello8620
@georgeokello8620 Год назад
What the hell kind of question is that? I have even worked in actual companies and most companies especially at enterprise level have far worse code standards ranging especially from legacy code level and the way how teams are structured to deal with client feature requests. This video deals with just application of coding concepts while coding for company introduces complexities such as trade offs for code and analysis in juxtaposition with the companies resources and their project delivery structure which is a whole different thing.
@matthias916
@matthias916 Год назад
@@georgeokello8620 it's a question that's not related to the video nor directed at you, I feel like that was made pretty clear in my original comment
@rutabega306
@rutabega306 Год назад
Wait is this really your second video? It's excellent quality, subscribed!
@sahandjavid8755
@sahandjavid8755 Год назад
Most of my time in companies I worked for goes into convincing engineers not always repeating is bad! If you have only 2 cases for your found pattern, it doesn’t mean you need to create shared code! If you don’t have a complete insight over your problem or future requirements do not abstract!! Thank you for the video, The idea is to grow together and then we can do great things.
@Smilezlol
@Smilezlol Год назад
I honestly can't tell if this is a joke video or it was simply meant to bait people (consider me baited), with the video starting with "For every bit of abstraction you add, you've added more coupling". No, abstraction means literally the exact opposite. If your abstractions are making your code more coupled then you need to learn some better design patterns and stop using so much inheritance.
@eidiazcas
@eidiazcas Год назад
Agree, thank you
@JosepPi
@JosepPi 4 месяца назад
The way I usually go about this is, by default, having different scripts with duplicate code. The moment I try to do something with those scripts in which an abstraction could help me do it faster, I stop whatever I am doing, and create that abstraction. There is a little downside to this which is that you have to create all the systems from the beginning and not really overdevelop one side of the app without making sure the rest is in a basic state and interconnected. This way, you can catch these potential abstraction classes early and you don't have to change a lot of things. I don't see this working for bigger applications, thought. Actually, today, I had to do this in my tower defence game. I was creating one script for all the turrets, another script for all the barracks and another script for all the farms. Then I had to create a very basic UI and realized if I had a building parent class that contains the name, description and cost of every building, I could save me some time developing it their UI. We will see if in a year, I regret this decision, but for now it does feel great to find these little shortcuts.
@unitydeveloperkgk9836
@unitydeveloperkgk9836 Год назад
This is very solid how you described abstraction as a tradeoff with coupling. Its reinforced what I was wondering as to, do you really create an interface or abstraction where your going to use it for only possibly two classes and without over engineering future edge cases and try to plan for every possible scenario.
@sgerodes
@sgerodes Год назад
Please dont stop creating content. Your videos have amazing value
@another7please
@another7please Год назад
You have the point of the save(GameState) interface abstraction backwards, the point of that abstraction is to force the coupling between the rest of the game and the specific serialization method to nothing, because you communicate through the interface instead of directly with the SaveJson or SaveXML class. You then have dependency injection to swap out or add whatever serialization implementation later with a guarantee that you won't have to decouple anything from the previous or edit any existing code beyond adding the class to a list. It doesnt make sense to call having to have a function that's already present in the concrete classes increased coupling, if anything it's less coupling because without the interface the game or factory must know the specific interface of all concrete classes instead of just one uniform interface. Also, your factory design actually increases coupling because you now have to extend an enum, add some config data, change the caller to refer to that enum case, then add your specific enum case into the switch statement to call a specific concrete function that's uniform across all classes. Again, it's less coupling for the factory to have a serialization interface because there would only be one function to couple with. But in reality the factory would be superfluous with an interface because it just increases complexity for no gain. So all you've done is taken the small coupling that would've constrained your concrete serialization classes and spread it across your entire codebase instead. The fact that you ended up adding the abstraction back for the timer proves that there is less coupling with an interface. I do agree that abstractions or indirection is not the answer to every problem but this was a really poor example.
@TheIllerX
@TheIllerX Год назад
Thanks for the video and for bringing up this topic, which I think is massively overlooked in general. In school and the early years of programming one often get the impression that "Code duplication is bad, must do abstraction" without ever thinking about coupling issues and about creating unnessecary complexity in the system. Sure, code duplication might be bad, but that doesn't mean more abstraction is the solution, especially if the abstraction means more inheritance structures in some object oriented language. I have often been swearing at old code that cannot be upgraded or modified just because the thing I want to modify is stuck in some strange structure with a lot of dependencies/couplings. Some contained free utility functions doing one thing and doing exactly what it is said to do without any couplings to other code is often a better alternative to more abstractrion. In that way one can avoid code duplication without increasing the couplings. Coupling really is an enemy to all upgrades of the code. Everyone will become more and more hestiant to do any changes since it might break something. In that way they whole codebase has a big risk of becoming obsolute and out of date.
@christopherbinder8967
@christopherbinder8967 Год назад
In the real world I would suggest to use dependency injection for the file saver. In this case you have no coupling and can write unit tests more easily.
@Nicholas-qy5bu
@Nicholas-qy5bu Год назад
Abstraction can also be used to reduce coupling with dependencies, but i totally agree with you when you are designing your "business/game" logic it can increasing coupling which can be dangerous without any actual benefits. One other point you didnt mention is with generic purpose classes, if not designed/used correctly it can make the code harder to understand for what use cases it is used for. A simple exemple i can think of is using the same generic entity with a 'type' property to handle differents use cases, instead of having one entity for each, i see this a lot, and it create its own lots of problems ( storage and data manipulation ), while forcing you to use switch statement in your code base which is not a good idea for maintainable code.
@jorgecarcamo2658
@jorgecarcamo2658 Год назад
I'm still learning, however, here is my opinion: the problem with saveWhatEver isn't the abstraction itself, but a design problem related to the responsibilities. The coupling derives from mixing two responsibilities in one class. You have two different actions, converting an object into a JSON/XML representation and saving a file.
@carlesmolins3269
@carlesmolins3269 Год назад
Imo, in the example of the save method it brings value, as defining a common interface means that both classes can be used interchangeably in the main code, thus actually reducing coupling.
@Bloodthirst
@Bloodthirst Год назад
my problem with abstraction is that a LOT of the times it's done very poorly in a way that makes it an obstacle in the code/program rather than a tool. I've seen a lot of programmers (juniors AND seniors) fetishize and abuse interfaces while using "separation of concerns" and "cLeAn CoDe" as the end all be all arguments , like do you really need an IManager that inherits ISystem that manages ICustomers (which are also ICurrencyHolder btw) which contains ICurrency just to say that "a customer can have x amount of money?". I've seen "IMovable" and "IRotatable" in a codebase of a game just because "well some objects like doors only rotate, and some objects like bullets on move , sooo yeah gotta make sure EVERYBODY knows that now" ... Abstraction is only good when you know how to do it correctly and it serves a valid and concrete purpose , if not then it's a just hurdle to go through just because the writer of the code "felt like it"
@SamuelLopez-mr5br
@SamuelLopez-mr5br Год назад
>I've seen "IMovable" and "IRotatable" in a codebase of a game just because "well some objects like doors only rotate, and some objects like bullets on move , sooo yeah gotta make sure EVERYBODY knows that now" It's called Interface segregation principle, I dont see what it has to do with having poor abstraction.
@DuniC0
@DuniC0 Год назад
Hi, this is a very interesting topic with a really short conclusion in my opinion. I see trading between abstraction and coupling as an oversimplification. There are multiple ways of fighting coupling and code repetition: abstraction as in inheritance, better abstraction with interfaces, composition, bridges, callables/runnables/callbacks/closures, proxies, event driven programming, etc.
Далее
The Flaws of Inheritance
10:01
Просмотров 897 тыс.
Dependency Injection, The Best Pattern
13:16
Просмотров 746 тыс.
skibidi cat pedro 🐈🔥  #skibiditoilet
00:20
Просмотров 2,3 млн
Don't Write Comments | Prime Reacts
14:31
Просмотров 203 тыс.
Giving Personality to Procedural Animations using Math
15:30
Naming Things in Code
7:25
Просмотров 2 млн
25 nooby Python habits you need to ditch
9:12
Просмотров 1,7 млн
How principled coders outperform the competition
11:11
Dear Functional Bros
16:50
Просмотров 460 тыс.