Тёмный

Composable Code Can Be Simple - Intro to dependency diagrams and composition 

Essential Developer
Подписаться 38 тыс.
Просмотров 14 тыс.
50% 1

★ Register Now to Our Free iOS Career Course! www.essentiald...
Subscribe to our channel: www.youtube.co...
In this episode, we continue exploring the dependency inversion principle and composition, while documenting our system's evolution in diagrams.
► Learning outcomes
• Understand basic depiction of dependencies, abstractions and concrete types in diagrams
• Understand how diagrams translate into code and vice versa
• Understand how the SOLID principles and composition are applied through the examples
• Understand differences and similarities between closures and protocols as abstractions in Swift (unnamed type signatures vs. strictly named types)
► Visualizing the dependency graph
Visualizing our code in diagrams can be a great framework to think while modeling the design of our systems. The early visualization of the design can make an excellent tool for communicating ideas to our colleagues and planning/coordinating a team effort. Diagrams can also help with independent development, as the team can quickly plan new features design (5-10 minutes on a whiteboard), build them in parallel and later integrate the pieces without conflicting with the work of other developers.
Moreover, diagrams serve as a great diagnostic tool as they embody the dependency graph of components and modules, revealing tight coupling and even retain cycles that could potentially lead to rigidity and memory leaks. Learning how to draw and read diagrams is an excellent addition in the skillset of developers that aim to create sustainable codebases.
• ViewControllers pt.1: Storyboards composition • Composing View Control...
• ViewControllers pt.2: Testing storyboards • Composing View Control...
★ Professional iOS Engineering
Take your skillset to the next level and boost your career www.essentiald...
★ Download our new app
You can download Neat Trivia from the App Store today at itunes.apple.c....
Connect with us on:
• Website: www.essentiald...
• Twitter: / essentialdevcom
• Facebook: / essentialdeveloper

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

 

17 сен 2024

Поделиться:

Ссылка:

Скачать:

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

Добавить в:

Мой плейлист
Посмотреть позже
Комментарии : 50   
@gasparfreak
@gasparfreak 2 года назад
Thanks for this example for us beginners, much appreciated.
@EssentialDeveloper
@EssentialDeveloper 2 года назад
Glad it was helpful!
2 года назад
Definitely instructive! Should teach this at universities.
@tityseptiani8584
@tityseptiani8584 5 лет назад
Another great video! I really enjoy your channel because you are very different from other iOS channels around youtube that only talk about how to create this and that, but never explain about the most basic things to become a good programmer. I mean, beginners should really look at your videos so they will not become the spaghetti coders, just like how I was before I discovered your channel. I'm getting more and more understanding about clean architecture and what not to dos from you. So, thank you very much for putting the efforts to create all these videos. Anyways, I read that it's better to create objects using abstract factories. From your example, can I use factories to create the LocalFeedLoader and RemoteFeedLoader? Should I create another protocol like FeedLoaderFactory and implements it on a concrete class? Is it necessary or it complicates things instead? How would you do it?
@EssentialDeveloper
@EssentialDeveloper 5 лет назад
Hi Tity, thanks! We're glad the videos are helping you improve as a developer. Abstract Factories can be very helpful, but can indeed complicate our codebase. We used abstract factories in the Quiz App series ( www.essentialdeveloper.com/professional-ios-engineering-season-1 ) to decouple the Routing module from the UI module. And we compose Routing + UI in the Main module (where the factory implementation lives for now). In our case, the modular approach paid off since we wanted to have different Quiz applications and reuse as much code as we could. Every technique has a trade-off so we recommend you to use it if it'll improve your codebase, instead of using it just for the sake of using it. The problem is that we often have to make a lot of mistakes before we become good at judging when to use a technique or not. "We mostly learn by failing." And that's not a bad thing! We encourage you to try out different techniques in a safe environment, like a personal project (or even better, maybe at work you have space to do so). Don't forget to keep asking yourself "Is this approach really improving the design? Is it elegantly solving the problem? Is it making my (and my colleagues) work simpler/easier/better?" ✅⛩
@tityseptiani8584
@tityseptiani8584 5 лет назад
Essential Developer I see. I am planning to rewatch your Quiz App tutorial because now I feel more confident about understanding it more. It used to be a little confusing for me. Thank you!
@calosth
@calosth 5 лет назад
I always think about I need learn about how to diagrams. Thanks for this, I really enjoy this content about software architecture
@JawadAli-iq2db
@JawadAli-iq2db 2 года назад
Hey Caio, Thanks for this amazing video... I have a small question... Where would we save the feed? Like how that saving mechanism would work? Where would we place the code to save the feed? In RemoteWithLocalFeedLoader or somewhere else?
@EssentialDeveloper
@EssentialDeveloper 2 года назад
Hi! It'd be in a difference component. We show it in detail in the iOS Lead Essentials program. Check it out if you're interested: iosacademy.essentialdeveloper.com/p/ios-lead-essentials/
@willasaskara
@willasaskara 3 года назад
Thanks a lot!
@gulyaboiko
@gulyaboiko 5 лет назад
Thank you for the video! But I don't understand where should I put code to fill the DB
@nonameplum
@nonameplum 5 лет назад
At 8:23 you have started to discuss a very interesting topic of possible modules structure. I wonder how would that work in the case as on the diagram because most of the time the FeedViewController with FeedLoader protocol will live in the "main module". The RemoteFeedLoader will be in the second bundle but to be able to implement FeedLoader protocol it will need to depend on the main module to use the protocol type. On the other hand "main module" will need to also add dependency of the module that contains RemoteFeedLoader. It becomes circular dependent. Do I get it correctly? To solve that problem we could move the protocol to also separated module that both main module and RemoteFeedLoader module will depend on, but seems like over complicated.
@EssentialDeveloper
@EssentialDeveloper 5 лет назад
Hi Łukasz! The FeedViewController + protocol can live in the "Main module" but don't have to. We have many options. We could create a "Feed feature" module, for example. Then the API (RemoteFeedLoader) module would depend on the Feed module, and we would have the Main module as just a composer of (Feed + API) modules. If needed, we can also decouple both the Feed and API modules from each other, with an extra (tiny) adapter module in between. Or the adapters can live in the Main module. As you said, moving the protocol to a separate module is also an option, but can be overkill in many cases. There are other options, but they all depend on the needs of the system. Initially, we'd prefer to bundle all types in the same module until we have more information how to break them down into better components (depending on how they are going to be reused, for example). Although they would live initially in the same module, we'd still create a good separation of concerns with clear protocols/interfaces, so it's easy to develop, collaborate, maintain, test and break it down when needed. ✅⛩
@nonameplum
@nonameplum 5 лет назад
Hi! Thanks for the detailed answer. Seems like a good topic of how to break down the app into modules aka frameworks. Most of the iOS application projects probably don't use that technique often because it seems to be too hard. The main reason is the broken part of the hard coupling of the "virtual" modules. It is very easy to do because all the types live in the same big module and this reduces the short-term duration of the task but breaks the rules which are important for the long-term goals. When you force yourself to use frameworks to break down the pieces (even separated views with features) into separated frameworks the environment forces you to think about dependencies and interfaces. I like the idea of separating "Feed feature" into the separated framework from the main module and keep the main module as a composer. On the other hand it complicates things a bit when you have a bunch of helper extensions for UIView, UIViewControllers, String and a lot more other which will need to be also moved to separated module from the "main module" to be able easily to use in "Feed feature", maybe "main module" and others. I wonder if there are more ideas that could be used here to keep the balance between usability and good separation. Btw, great video as usual!
@EssentialDeveloper
@EssentialDeveloper 5 лет назад
Hi Łukasz, thanks! There are undoubtedly others options, but sadly no silver bullets. Software Development is very similar to engineering where we are constantly challenged with trade-offs. As you said, we are continually managing quality, cost, risk, etc., in the short and long-term. Even when we're unaware of that! Since we're often balancing trade-offs, the more options we have in our arsenal, the higher the chance we have to make better decisions. We wouldn't say it's inherently "hard to do," but mostly hard because it can be quite *unfamiliar* to many. And a lot of the complexity comes from the fact that our tools are not very helpful in that sense (looking at you, Xcode). Our advice to developers is to join the Mastery and Continuous Learning journey! ✅⛩
@dipesh77777777777777
@dipesh77777777777777 3 года назад
Kairo Can you guys create a video how to proceed to clean up the code if some one gets completely messed up code , Sort of a process which can accelerate the code clean up without affecting much with deadlines, I think this is something everybody encounters in most of the organization
@EssentialDeveloper
@EssentialDeveloper 3 года назад
Hi Dipes! Do you have a messy codebase we can use as an example?
@dipesh77777777777777
@dipesh77777777777777 3 года назад
@@EssentialDeveloper NO, I am just wondering how exactly you people proceed to fix messy code base
@joserobertoabreu4877
@joserobertoabreu4877 5 лет назад
Great video! Another option that could help to simplify the interface is using chain of responsibility design pattern, basically each FeedLoader can have an optional fallback loader.
@EssentialDeveloper
@EssentialDeveloper 5 лет назад
Hi José, thanks! Chain of responsibility is indeed another option. We can also implement it without adding the optional-fallback to all `FeedLoader`s (we could just compose them in another type with an array of fallbacks so, every time a loader fails, we try the next one in the list until we either get a valid result or we reach the end of the array). ✅⛩
@abuzeidibrahim
@abuzeidibrahim 5 лет назад
it's not clear for me, is that what you mean? protocol FeedLoader { var nextFeedLoader:FeedLoader?{get set} func loadFeed(data:(([String])->Void)) } class FeedDataSource:FeedLoader{ var nextFeedLoader: FeedLoader? var remote: RemoteFeedLoader! var isConnected:Bool{ return false } func loadFeed(data: (([String]) -> Void)) { if isConnected{ remote.loadFeed(data: data) }else{ nextFeedLoader?.loadFeed(data: data) } } }
@abuzeidibrahim
@abuzeidibrahim 5 лет назад
​@@joserobertoabreu4877 Sounds Great, Thanks for your effort, would you give me your twitter account?
@EssentialDeveloper
@EssentialDeveloper 5 лет назад
Another example is how UIKit handle events in the responder chain. developer.apple.com/documentation/uikit/uiresponder `var next: UIResponder?` "Returns the next responder in the responder chain, or nil if there is no next responder."
@pawelpiotrgil
@pawelpiotrgil 4 года назад
I'm a bit late to the party, but for me the big challenge in designing a good, clean architecture in Swift is error handling. For me the way the language approaches error handing is not helping with separation. For example in this app - if FeedLoader could fail - how would you approach this? On one hand you have different error scenarios when loading from local storage, but there is a completely different set when networking is involved. How do you futureproof error design so it doesn't create implicit dependencies and break open-closed? Let's say that for now if error happens the user should only get prompted with a simple alert - not super hard. But I can easily imagine that in the near future the PO will come and say "let's put an alert with additional button for networking errors". Now it's getting hard, especially when you have project with presentation and usecase separated - should we perhaps create a completely separate dataflow for errors and introduce some kind of error streams to avoid errors in application business rules? But then the problem emerges - what if in some cases error handling is not just UI, but also affects some business rules? It boggles my mind since I switched to Swift
@EssentialDeveloper
@EssentialDeveloper 4 года назад
Hi Paweł! If it's a meaningful 'Domain' error, you should probably create a rich type representation for it and pass it through the use cases and presentation. On the other hand, network errors are usually infrastructure implementation details that shouldn't leak into use cases. But that's not a problem. For example, you can have a separate pipeline/flow for those errors - or even send network errors directly to the presentation layer, which can map/translate them to view data that can be rendered on the screen (e.g., a message with 2 options).
@pawelpiotrgil
@pawelpiotrgil 4 года назад
@@EssentialDeveloper That's exactly how I implement those scenarios - only inform usecase that I/O failed and send errors in other stream. Cool to get confirmation for this approach :) Thanks for the reply :)
@jorgeivesmurilloparedes1906
@jorgeivesmurilloparedes1906 2 года назад
Great video, what tool do you use to draw the diagrams?
@EssentialDeveloper
@EssentialDeveloper 2 года назад
Hi Jorge. We used draw.io
@imamad
@imamad 21 день назад
But which tool you personally used to draw the diagrams?
@EssentialDeveloper
@EssentialDeveloper 21 день назад
Hi! We used draw.io/
@abuzeidibrahim
@abuzeidibrahim 5 лет назад
Good job, we waiting for more great videos :)
@rohitdhawan02
@rohitdhawan02 4 года назад
Very nice, but can you guys make a video like how to make frameworks for modular approach of our application and how to handle image and fonts which is shared among different modules or framework. Second how different modules or framework will interest each other. I will be highly obliged. Thanks
@EssentialDeveloper
@EssentialDeveloper 4 года назад
Thank you for the suggestion ✅
@KashmiriFoodsPk
@KashmiriFoodsPk 3 года назад
Now I'm more comfortable with the diagrams :D
@jakubbakalarz5648
@jakubbakalarz5648 Год назад
In remotefeedloader and localfeedloader shouldnt we call completion(loaded_array_of_strings) after //do something? Because now we pass completion closure to load function but we dont execute it anywhere.
@EssentialDeveloper
@EssentialDeveloper Год назад
Hi! Yes, of course. Calling completion is part of the unimplemented "do something" portion.
@alaaajoury94
@alaaajoury94 Год назад
What's the name of the UML diagram program that Caio uses?
@EssentialDeveloper
@EssentialDeveloper Год назад
Hi! We used draw.io
@josefsantamaria
@josefsantamaria 4 года назад
If I could like your videos multiple times I would do it 1k times
@EssentialDeveloper
@EssentialDeveloper 4 года назад
Thank you, Josef! Glad you like our content.
@yoeljimenezdelvalle8793
@yoeljimenezdelvalle8793 5 лет назад
in 15.17 you rewrite to load(completion) should not be completion(load), or perhas you could make a video about closure and explain why is like that instead od my understanding, even that is a great video.
@EssentialDeveloper
@EssentialDeveloper 5 лет назад
Hi Yoel! `load(completion)` is the correct form since we are invoking `load` and passing a `completion closure` as a parameter. For example, it's the same as calling `remote.loadFeed(completion: completion)` - notice how we pass the `completion closure` as a parameter to the `loadFeed method`. Does it make sense now? ✅⛩
@vitaliy2540
@vitaliy2540 4 года назад
hi guys, Which tool do you use for draw UML diagram?
@EssentialDeveloper
@EssentialDeveloper 4 года назад
draw.io ✅
@ciantim2996
@ciantim2996 4 года назад
Sorry if it's a dumb question, but I want to ask you a question : Why RemoteWithLocalFallbackFeedLoader class depends on concrete type but not abstract type (FeedLoader) thus allow us to inject another implementations? Is it because RemoteWithLocalFallbackFeedLoader, RemoteLoader and LocalLoader live in the same level ?
@EssentialDeveloper
@EssentialDeveloper 4 года назад
Hello! That's a great question. In this case, the goal was to compose the remote and local loaders in a specific way: "first, try to load from remote, if it fails, load from the local cache." Thus, the RemoteWithLocalFallb­ackFeedLoader is a Composite implementation that enforces this specific behavior at compile time with the concrete types: `init(remote: RemoteLoader, local: LocalLoader)` This means the compiler will enforce the right composition for our use case. However, if you want to allow different types of composition, you can create a composite that depends on the abstract FeedLoader type: `init(primary: FeedLoader, fallback: FeedLoader)` This way, you can still compose a remote loader as the `primary` loader and a local loader as the `fallback` loader to achieve the behavior same behavior. But you'd also be able to compose the system in different ways, such as to "first, try to load from local cache, if it fails, load from the remote." It depends on what you're trying to achieve. The concrete option gives you composition guarantees at compile time. The abstract option doesn't, but it's more extendable. ✅
@kduy14
@kduy14 4 года назад
Using concrete type at this point makes more sense than an abstract one. Since in RemoteWithLocalFallb­ackFeedLoader, we implement specific behaviors in a specific order, forcing callers to pass exact Remote and Network Loader is a thing should be done. Imagine with `init(primary: FeedLoader, fallback: FeedLoader)`, they leave Local Loader as the primary one, the logic will be broken. On the other hand, abstract types are more extendable - I totally agree but not this case.
@ciantim2996
@ciantim2996 4 года назад
Yes. Got it !
@duoduo8213
@duoduo8213 4 года назад
May I know the difference between dotted-line and solid-line in the diagram? are there any convention when to use which?
@EssentialDeveloper
@EssentialDeveloper 4 года назад
Hello! A solid line denotes a strong relationship between components, such as inheritance, aggregation, or composition. A dashed line denotes a weaker relationship between components, such as protocol/interface implementation or weak dependency. Watch this video to learn more: ru-vid.com/video/%D0%B2%D0%B8%D0%B4%D0%B5%D0%BE-Mk34R-Q9-RE.html
Далее
How would you react?!😳
00:44
Просмотров 5 млн
ВЛАД А4 СКАТИЛСЯ
09:34
Просмотров 509 тыс.
How safe are Swift structs?
12:08
Просмотров 14 тыс.
Why I won’t need constructors anymore in C# 11
9:39
Просмотров 135 тыс.
"Clean" Code, Horrible Performance
22:41
Просмотров 887 тыс.