Abstraction Can Make Your Code Worse

  Рет қаралды 593,093

CodeAesthetic

CodeAesthetic

Жыл бұрын

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

Пікірлер: 1 100
@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 kzbin.info/www/bejne/qImTeqeMerudfsU
@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.
@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 4 ай бұрын
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"
@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.
@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!
@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.
@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 8 ай бұрын
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 ❤
@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.
@maybepumpkins
@maybepumpkins 4 ай бұрын
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.
@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 11 ай бұрын
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 10 ай бұрын
@@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 5 ай бұрын
@@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 3 ай бұрын
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.
@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.
@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 10 ай бұрын
Java supports higher-order functions now, maybe try using them although you will have need pass through for arguments.
@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.
@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.
@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.
@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 11 ай бұрын
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 10 ай бұрын
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.
@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!
@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.
@ciberman
@ciberman Жыл бұрын
I REALLY love this way of showing code. Explaining what are you doing while showing a speed up video of the changes.
@VitorMach
@VitorMach 2 ай бұрын
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.
@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.
@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.
@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.
@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.
@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.
@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.
@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.
@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.
@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 :)
@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.
@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))`.
@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!
@TheMyHalo
@TheMyHalo Жыл бұрын
I just found your channel and love both videos. Really hope youre going on with this style
@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
@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 10 ай бұрын
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 5 ай бұрын
@@aoeu256 because why would you do that? lol, C is the king
@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.
@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.
@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")
@LordErnie
@LordErnie 3 ай бұрын
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.
@_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...
@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.
@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.
@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.
@yuryzhuravlev2312
@yuryzhuravlev2312 11 ай бұрын
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.
@stuartferguson11
@stuartferguson11 7 ай бұрын
A saver class is probably going to call a save method on the game objects, and provide methods for writing state values like numbers, strings, etc. In that case using an interface (abstract class in C++, protocol in Swift) would be my choice. Both savers can conform to the interface without sharing any code.
@tally3018
@tally3018 Жыл бұрын
Just found ur channel. Looks like ur doing absolutely great after only 2 weeks uptime! Subscribed!
@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
@TheGamezbePlayed
@TheGamezbePlayed Жыл бұрын
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.
@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.
@notreallyasloth
@notreallyasloth Жыл бұрын
Your videos are so good. Can’t wait to see what you do next ❤
@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.
@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.
@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.
@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.
@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".
@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 ?
@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.
@yutubl
@yutubl 8 ай бұрын
I made best expierience implementing data specific serialization (also save + load) methods in each class which data/attributes have to be serialized/saved/loaded early in the software project. 0. when your software architecture class design is stable enough (no daily complete redesigns keeping you away from implementing these classes) 1. serialization methods for saving and loading keeps you back from re-implementing new file format methods per each class which has to be serialized it data attributes (DRY). I think a good serilization method interface might support parameter for: 1.1 storage/file/stream method/functor parameter implementing file format; 1.2 content filter method/functor parameter selecting which object internal data attributes; 1.3 if you always serialize per each class / object a specific version in the saved file format, building and maintening downward compatibility to your 1st (experimental) early file formats versions are possible, so you don't need to throw away saved file contents, giving you options of converting into latest file format version. Here a defined default class constructor and default class loader with exact default values per attribute helps. 2. your project development process benefits early from saveable (and re-loadable) classes, 3. all testing become much easier: manual tests get faster and you can begin automated test 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.
@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.
@perchful3872
@perchful3872 Жыл бұрын
Dude! I love this video!! I think especially for less experienced engineers like me, this kind of thinking is ABSOLUTELY what takes the longest. Thanks for the video!
@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.
@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.
@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
@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.
@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.)
@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.
@exploringgodscountry
@exploringgodscountry 2 күн бұрын
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.
@cassandrasinclair8722
@cassandrasinclair8722 7 ай бұрын
Counter argument for the "save" interface. Instead of choosing or constructing; pass the object as a Save Object, i.e. do dependency injection.
@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
@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!
@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.
@techkev140
@techkev140 Жыл бұрын
Seriously good points about abstracting away common functionality. However i had to watch through a second time as the highlighted code did not always standout. Trying to take in everything on screen when the surrounding code was blurred out, i found the highlighted code you were talking about didn't standout and i only focused on it as you moved on to your next point. The second viewing of the video was better as i knew what i was looking for in your examples. Good video, thoughtful.
@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.
@keokawasaki7833
@keokawasaki7833 Жыл бұрын
Don't abstract away code, abstract away the idea
@Nerdsown
@Nerdsown Жыл бұрын
Excellient video. Very succient. I have recently been attempting to reduce coupling by allowing for some repetition but I hadn't come across someone else in favour of it yet. In fact I have recently encountered the exact save and load scenario you did. I have a SaveFileReadWriter which manages 2 other ReadWriters (JSON and legacy XML). These are intentionally completely decoupled. We need to always attempt to reduce cognitive load on the next person. So we need to ask ourselves, if we change logic in one, how likely is it we will want to make the same change to the other? If its unlikely, then abstraction would mean we need to check that we didnt accidently change the other more often then not. So its increasing effort, not decreasing.
@aaronclement1266
@aaronclement1266 10 ай бұрын
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.
@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!
@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.
@cid-chan-2
@cid-chan-2 Жыл бұрын
I've been writing the comment the second time since I don't know how to best put it. No. Coupling is an inherent trait of Inheritance. Not of abstraction. There is more than one way to abstract code then to subclass it. If your entire definition of abstraction reduces to subclasses, you miss a few other options. Like extract to functions. Or traits/interfaces. There is a rule I have: If the class only has one public method, it's a curried static function. Convert that to a static function first. That would have nipped the "FileSaver" class in the bud. Then have an ObjectSaver interface that calls the static function. Martin Fowler calls that a repository.
@volchonokilliR
@volchonokilliR Жыл бұрын
I think this topic has more depth and use cases, but I agree with general idea presented in this video. There is a cost and tradeoff that show up in different ways for every practice, and that's not often discussed when these subjects are touched. Many people just seem to be hung up on the idea that something is "bad", or that something always "has" to be in certain way
@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.
@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...
@sgerodes
@sgerodes Жыл бұрын
Please dont stop creating content. Your videos have amazing value
@friedrichwaterson3185
@friedrichwaterson3185 Жыл бұрын
Such an awesome thematic ! I hope your channel grows fast.
@benbertheau
@benbertheau Жыл бұрын
i just love your animation style very simple and stylish
@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
@chewbaccarampage
@chewbaccarampage Жыл бұрын
I recommend Chris Keathly's talk on how applying S.O.L.I.D. increases interaction points. Both concepts are in direct tension. So like everything in programming, it depends...
@nicolabombace2004
@nicolabombace2004 Жыл бұрын
The coupling and associated limitations are a consequence of using inheritance to model relationships that are not is-a. Think of it another way: The SaveJson class has-a filesaver, now the filesaver can become a private member variable initialized at construction. This is the composition/dependency injection duo, and alleviates the problems you just mentioned.
@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.
@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.
@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.
@zactron1997
@zactron1997 Жыл бұрын
The best advice I've seen is to write code that expresses what you mean, not what you think has to happen. If you need to loop through a collection of numbers, you should probably use the built in iteration methods, because they represent what you mean to do. This is in contrast to setting up a while loop, or a for loop, or copying and pasting a manually unwrapped loop expression. I often hear newer programmers worry about performance with this philosophy. In my experience, well expressed code that works is easy to wrap and optimise. Optimised code is, however, mostly impossible to fix if it doesn't work.
@Zoomakroom
@Zoomakroom 4 ай бұрын
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.
@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
@michalismichaelidis2585
@michalismichaelidis2585 6 ай бұрын
Amazing videos so far! I totally agree with your video! you could make another video comparing very coupled and deduplicated horizontal service heavy code (e.g MovieService) that goes well with anemic models (with just getters and setters) versus a bit more vertical decoupled classes with just one public method and some private ones that are actually decoupled and scream what the program actually does (that is not CRUD).. the common logic can be in the domain/data models.. This has led to so much easier code to understand, test, maintain and even onboard new employees but companies are so reluctant following it since they think it is DDD or hexagonal or that it creates so many files, or that this is not OOP (!!) and it is supposedly confusing.. In my experience, when there is business logic that is not just CRUD, we prefer less coupling and slightly more duplication than a thick layer of horizontal services that usually suffer from cyclic dependencies just in favor of code deduplication
@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.
The Flaws of Inheritance
10:01
CodeAesthetic
Рет қаралды 866 М.
Premature Optimization
12:39
CodeAesthetic
Рет қаралды 742 М.
【獨生子的日常】让小奶猫也体验一把鬼打墙#小奶喵 #铲屎官的乐趣
00:12
“獨生子的日常”YouTube官方頻道
Рет қаралды 82 МЛН
INO IS A KIND ALIEN😂
00:45
INO
Рет қаралды 18 МЛН
SMART GADGET FOR COOL PARENTS ☔️
00:30
123 GO! HOUSE
Рет қаралды 22 МЛН
Dear Functional Bros
16:50
CodeAesthetic
Рет қаралды 424 М.
Every CSS Animation property
9:26
chunkydotdev
Рет қаралды 39 М.
Why You Shouldn't Nest Your Code
8:30
CodeAesthetic
Рет қаралды 2,5 МЛН
Don't Write Comments | Prime Reacts
14:31
ThePrimeTime
Рет қаралды 195 М.
How principled coders outperform the competition
11:11
Coderized
Рет қаралды 1,5 МЛН
Naming Things in Code
7:25
CodeAesthetic
Рет қаралды 1,9 МЛН
"Clean" Code, Horrible Performance
22:41
Molly Rocket
Рет қаралды 827 М.
Never install locally
5:45
Coderized
Рет қаралды 1,5 МЛН
Giving Personality to Procedural Animations using Math
15:30
t3ssel8r
Рет қаралды 2,3 МЛН
【獨生子的日常】让小奶猫也体验一把鬼打墙#小奶喵 #铲屎官的乐趣
00:12
“獨生子的日常”YouTube官方頻道
Рет қаралды 82 МЛН