The One Thing You Cannot Do in Object-Oriented Programming but You Can in Functional

2023 ж. 27 Мау.
9 186 Рет қаралды

Download the source code for this demo at Patreon: / one-thing-you-do-85222122
In Object-Oriented Programming, objects are instances of classes and they encapsulate behavior and state - that is what every textbook on OOP is teaching. However, there's a subtle limitation in OOP when it comes to dealing with changes in the state of these objects.
We often try to mitigate that limitation by applying the State design pattern, but that adds extra complexity and potential confusion to downstream components, especially when using Object-Relational Mappers (ORMs).
In this video, you will learn to recognize the fundamental limitation of Object-Oriented Programming and how Functional Programming (FP) offers an inverse perspective. Through immutability and transformations of values, FP tends to make state transitions explicit and predictable, leading to a more straightforward way of handling the issues that would otherwise be too large a burden for a corresponding object-oriented model.
This requires a shift in thinking and can introduce its own challenges and learning curves. It is a fascinating topic, shedding light on the fundamental underpinnings of OOP and FP, and their practical implications in software development.
Thank you so much for watching! Please like, comment & share this video as it helps me a ton!! Don't forget to subscribe to my channel for more amazing videos and make sure to hit the bell icon to never miss any updates.🔥❤️
✅🔔 Become a patron ► / zoranhorvat
✅🔔 Subscribe ► / @zoran-horvat
⭐ Learn more from video courses:
Beginning Object-oriented Programming with C# ► codinghelmet.com/go/beginning...
⭐ Collections and Generics in C# ► codinghelmet.com/go/collectio...
⭐ Making Your C# Code More Object-oriented ► codinghelmet.com/go/making-yo...
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
⭐ CONNECT WITH ME 📱👨
🌐Become a patron ► / zoranhorvat
🌐Buy me a Coffee ► ko-fi.com/zoranhorvat
🗳 Pluralsight Courses ► codinghelmet.com/go/pluralsight
📸 Udemy Courses ► codinghelmet.com/go/udemy
📸 Join me on Twitter ► / zoranh75
🌐 Read my Articles ► codinghelmet.com/articles
📸 Join me on LinkedIn ► / zoran-horvat
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
👨 About Me 👨
Hi, I’m Zoran, I have more than 20 years of experience as a software developer, architect, team lead, and more. I have been programming in C# since its inception in the early 2000s. Since 2017 I have started publishing professional video courses at Pluralsight and Udemy and by this point, there are over 100 hours of the highest-quality videos you can watch on those platforms. On my KZhead channel, you can find shorter video forms focused on clarifying practical issues in coding, design, and architecture of .NET applications.❤️
▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
⚡️RIGHT NOTICE:
The Copyright Laws of the United States recognize a “fair use” of copyrighted content. Section 107 of the U.S. Copyright Act states: “Notwithstanding the provisions of sections 106 and 106A, the fair use of a copyrighted work, including such use by reproduction in copies or phono records or by any other means specified by that section, for purposes such as criticism, comment, news reporting, teaching (including multiple copies for classroom use), scholarship, or research, is not an infringement of copyright." This video and our youtube channel, in general, may contain certain copyrighted works that were not specifically authorized to be used by the copyright holder(s), but which we believe in good faith are protected by federal law and the Fair use doctrine for one or more of the reasons noted above.
⭐For copyright or any inquiries, please contact us at zh@codinghelmet.com
#csharp #objectorientedprogramming #functionalprogramming

Пікірлер
  • I struggled with this issue with oop for many years but as i have learned a bit of accounting, it helped me crack the issue. If you think of invoice as an object, you feel that it has behaviour. But invoice is simply a record as you call it in functional programming. What does it record? It records the services provided or items sold or a combination of both (example your car mechanic invoice which includes labour charges and parts sold). The payment for an invoice is a separate record and needs to be tracked separately. You may be required at some point in the life of the app how the payment was done (cash, card, wire transfer etc). There may be partial payment or there may be payment of more than one invoice through single instrument like a bank cheque. The state of the invoice is two additional attributes, if it is paid and if yes on which date, come from a different data source and should not ideally map to same table. A transactional database and a reporting database are two different designs. So if you are checking only one of them. If you are checking paid status of the invoices, it's coming from reporting db and that's a read only data. The payment operation you do on the invoice goes to transactional db and it would at times take a while to reflect in reporting db. So this is not as simple problem to club all these concerns into one sample. Oop at times requires deeper thinking of the domain to solve the problem correctly.

    @hemant-sathe@hemant-sathe10 ай бұрын
    • The application from the video is not the actual accounting application. As you have mentioned, in an actual business, we would store proofs of payments, rather than statuses. In that light, an invoice could also be partly paid, have an associated late payment interests, past dues, fees, etc. All those details would be too much for a KZhead video showing a technical issue in code.

      @zoran-horvat@zoran-horvat10 ай бұрын
    • The accounting ledger is the original immutable data store.

      @7th_CAV_Trooper@7th_CAV_Trooper2 ай бұрын
  • I haven't even heard of a record type in c#, I assumed you'd use structs for the functional approach. Time to do some more googling.

    @1992jamo@1992jamo10 ай бұрын
  • How does this solve the ORM problem? I see the same issue here. The type has changed. The object needs to update.

    @LE8271@LE827111 ай бұрын
    • There is the difference. In OOP, an ORM is tracking updates. When State pattern is used, that mechanism is broken and every caller of the State object must know that. In FP, ORM does not track updates, but it is rather used in a different operational mode. Therefore, these few classes would not be treated separately from others and their callers would have no new responsibility to implement. The whole code remains uniform with or without this feature.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat thx Zoran. frankly I fo not see where it is simpler still. You have to implement the update method both case. Dont you? Dont get me wrong, I am asking because I am learning.

      @LE8271@LE827111 ай бұрын
    • @@LE8271 It would be very difficult to explain the upsides of FP in a comment. There are other comments here where I tried it in more words. I would certainly point you to learn about functional modeling and you will advance, no doubt. The short version is that, in modern applications, one team defines the model and another team adds behavior on top of it. That is precisely how FP puts it, and exactly opposite to how OOP puts it. That is why C# is attaining more and more of the FP syntax in recent years. That method simply pays off in application development.

      @zoran-horvat@zoran-horvat11 ай бұрын
  • Hi Zoran. I really appreciate your videos. Thanks for taking the time to make them. I learned a lot from you. I agree with you that FP has many advantages over OOP when building a solid state machine. But in my experience, a state machine based on types is hard to persistent using an ORM. You can of course use a NoSQL document database but that is not an option for us. Instead, we use a state property and define the set of valid state transitions. The setter of the state property checks that the transition is valid and throws an exception otherwise. I am aware that this is not nearly as solid as your FP suggestion but it works and it is simply to model the state property with an ORM. How would you persist your record example with an ORM like NHibernate or Entity framework?

    @AndersBaumann@AndersBaumann8 ай бұрын
    • I Can relate with everything you said. I usually map records to an entity or to fields I side an entity to persist. EF Core has no proper support for them yet. Actually, immutable models already require explicit synchronization with the DbContext, so persisting states remains a lesser issue.

      @zoran-horvat@zoran-horvat8 ай бұрын
    • imagine as if it's the c tagged union. you can either store an int tag and a pointer to another memory location (table) or a tag along with data in a same memory chunk (it is possible to use blobs + serialization-deserialization to pull this off). of course, both techniques can be used simultaneously, just like in c unions. if you don't have that many tables and records, you can try to be smart about it and encode table number into the primary key (eg, first 8 bits could be the table number, rest -- the key itself), so that table could be derived from key without any db lookup.

      @Daniel_Zhu_a6f@Daniel_Zhu_a6f4 ай бұрын
  • I am not fully functional yet, but one thing I learn is that there are really BIG advantages in to separate "functionality" from "data". Rarely now my "Customer" class will have any methods, I will keep it very clean (99% properties) in order be easier to talk to Mappers, Orm, Mocks, etc, and most of functionality can be easily moved to Extension methods. Another thing that I find me doing is rarely creating deep hierarchies anymore: I think max 2 levels is very acceptable. C# is moving to a really great mix of functional and OOP, but using the best of both worlds!

    @ClickOkYT@ClickOkYT11 ай бұрын
    • That is the principal advantage of functional modeling over object-oriented. The domain is modeled with types and factories and behavior is implemented at places where it belongs and has a meaning, not to mention has access to other types specific to implementation.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • That advantage is so true and it feels natural too. Often in java code, I would naturally start to pull business logic out to utility classes or service classes, leaving my domain object pretty animic. I would have to force myself to keep logic in the domain, then there were issues with mutable objects. After coding javascript for a while, where all the data is in JSON format, converting this to a proper class is a pain. It's just a lot easier to keep the data and functionality separate.

      @CaptRespect@CaptRespect11 ай бұрын
  • Awesome video (as always)! I credit you to my increasingly growing interest of applying Functional patterns in C# after I watched your PluralSight course. Do you have example code that you could show a solution that is leveraging an ORM like EF for data persistence with the functional record types that accounts for the runtime type change? I'm trying to get my mind around the layer/plumbing needed to keep those to approaches in sync.

    @seankirk2397@seankirk239711 ай бұрын
    • I plan to make a video on that topic in the near future.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat thank you! Cant wait, you are my C# FP hero!

      @seankirk2397@seankirk239711 ай бұрын
    • @@zoran-horvat Yes, please! 🙂

      @kristianaranda@kristianaranda10 ай бұрын
    • @@zoran-horvat just wondering... is this video (ORM and FP) ever made? Can you please point us to the video?

      @WoonCherkLam@WoonCherkLam5 ай бұрын
  • This definitely an issue I have come across many times. The question in my mind is when will the ORM (or other persistence helpers) catch up with the FP approach. Right now if you are using EF the simplest and pragmatic approach would be to have the Invoice table have a nullable PaidOn column and let your C# model represent it that way too. This is not great OOP let alone FP. In general relation databases are themselves not great at representing an entity that has a life cycle. Only fields that should not be null at the beginning of that entities life can be declared not null. However, in reality the entity evolves and in other points of its life multiple fields can become mandatory (in fact the fact they are set at all indicates the entity's new "run time type" e..g a view on the invoice table where PaidOn IS NOT NULL becomes the PaidInvoice view).

    @codingbloke@codingbloke11 ай бұрын
    • This model, OOP or FP all the same, can end up in a single database table with a nullable column. The translation between the database row and multi-type or multi-class system, both ways, happens on transition across the application-database boundary.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat Yeah I get that but you have to give up on EF loading directly models right? EF only supports a one-to-one map from db entity to model. IOW we have more plumbing to build because currently the ORMs out there aren't quite what we need them to be.

      @codingbloke@codingbloke11 ай бұрын
    • @@codingbloke I think EF can still load the models directly. Syntactically, every model pulled from the DB is just an Invoice. But we can use type-checking to see which ones are Paid/Unpaid. We should separate the DB concerns from the domain. But I see what you're saying with the superfluous columns on your Invoice Table now, depending on its state.

      @nicknelson5372@nicknelson537211 ай бұрын
    • @@nicknelson5372 "type-checking to see which ones are Paid/Unpaid" That would be nice but that can't be done as far as I'm aware with the current state of EF. One table -> One type in a db context. What would be nice is to be able declare some mapping to a discriminated union and a table. Then be able to specify some rules about the table data that the mapper can use to determine exactly which type to construct. However if we were to go fully FP we would want a smooth way to let EF know "hey that immutable record instance is now replaced with this immutable record" with any of the types in the union being allowed.

      @codingbloke@codingbloke11 ай бұрын
  • HI Zoran, I do love these little snippet courses. I've been a fan of your style of teaching for many years through Pluralsight, first time to your channel though. I have used some of the functional programming styles from your courses on Pluralsight, particularly removing primitive obsession and immutable types. I have question regarding the PaidInvoice example, why would you have Paid method on already paid invoice that updates the date on which it was paid, surely that's wrong or am I missing something?

    @chrisbaker5284@chrisbaker52849 ай бұрын
    • In that example I thought of a possibility of updating the date while retaining the state of the invoice. But that logic is also flawed. If you want something to really think about, then the invoice should not know its status. There would have to be evidence of payments, and payment status of the invoice would be a calculated value, determined by summing up the payment evidence associated with that invoice. Even the association between payments and invoices is a tricky matter, oftentimes regulated by law.

      @zoran-horvat@zoran-horvat9 ай бұрын
    • @@zoran-horvat I hope you didn;t think I was criticising, as I wasn't, I was merely clarifiying my understanding, so thank you for that. I appreciate how hard it is to think of contextual examples without over complicating the issues. Thanks for all you great work.

      @chrisbaker5284@chrisbaker52849 ай бұрын
    • @@chrisbaker5284 I didn't take it as a criticism. I wanted to clarify that this design is too simple to be correct. There could be a handful of complaints to it, each perfectly reasonable. It is indeed hard to come up with a realistic business example that can fit a 10-minute explanation...

      @zoran-horvat@zoran-horvat9 ай бұрын
    • @@zoran-horvat Good, I wouldn't want to offend.

      @chrisbaker5284@chrisbaker52849 ай бұрын
  • A function in a functional programming language is equivalent to a single method interface in object oriented programming. An object in object oriented programming is equivalent to a closure in functional programming. You don't really miss anything using one or the other paradigm, but ease of use will definitely be an important factor in choosing the paradigm to use. Applying SOLID principles in OOP makes your code more similar to functional code, that is small objects and small interfaces with small state and small number methods and with little or no subclasses.

    @kdakan@kdakan11 ай бұрын
    • I agree with everything you said, with a note that must be added for completeness. There is one large distinction, and that is the fundamental distinction between OOP and FP: objects tend to expose void methods. That design decision is backed by ORM-s and, just to name two. The older I am, the more I dislike mutable design. This video is one in the series that underlines my preference toward immutable design, and in that case OOP and FP become equivalent in the way you have explained.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • I agree with everything you said, with a note that must be added for completeness. There is one large distinction, and that is the fundamental distinction between OOP and FP: objects tend to expose void methods. That design decision is backed by ORM-s and, just to name two. The older I am, the more I dislike mutable design. This video is one in the series that underlines my preference toward immutable design, and in that case OOP and FP become equivalent in the way you have explained.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • Joe Rainsberger in one of his talks said something like: "...with well designed OOP an object is just a set of partially applied functions". I like that.

      @ddanielsandberg@ddanielsandberg10 ай бұрын
    • @@ddanielsandberg I am not aware of that citation, but if I may guess it is based on observation that an object may combine its private state with the arguments and in that way act identically to a partially applied function in any functional language. In that sense, I agree fully with that quote.

      @zoran-horvat@zoran-horvat10 ай бұрын
  • Another question, why do you write Paid method in FP example as an extension method instead of a member of base Invoice type? Just because it looks good in this isolated state, or is there a technical (C# or compiler-wise) requirement that I miss to see?

    @XtroTheArctic@XtroTheArctic11 ай бұрын
    • The Paid method is following the fundamental principle of functional programming that types and behavior are defined separately. The principal advantage of that design paradigm is that each piece of behavior can be implemented at the place (layer, component, service, etc.) where it naturally belongs. On top of that, behavior would have access to specific elements it depends on, which might be inaccessible from the place where the types are defined. Opposite to that, an OOP design would force behavior along with the data, making extensibility possible mostly through subclassing. History is demonstrating that the former approach is better adapted to the needs of modern business applications.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat Thank you for the detailed answer again. As a long-time experienced programmer and software engineer, I myself, don't agree with your statement of "... the former approach is better ..." but, as I mentioned in my other comment, I respect your C# and programming experience greatly and, in my life, there have been many cases that I saw my mistakes which led to changing my knowledge and habits. For example, I was super against using Linq in C# long long time ago and that habit of mine changed for good many years ago. So..., for me to have a better understanding, you as an experienced OOP programmer, how and when was your transition into this approach (extension methods over type members) ? I mean how did you decide to switch?

      @XtroTheArctic@XtroTheArctic11 ай бұрын
    • @@XtroTheArctic That is not the switch. I use both approaches, each in its place. Virtual members are the trait of OOP. Extension methods (in C#, in particular) are the trait of FP. Take LINQ as a good example - it is hardcore functional, to the point that it does not have, and will probably never have, a method such as Do. Consequently, LINQ is implemented via extension methods.

      @zoran-horvat@zoran-horvat11 ай бұрын
  • This is pure gold!!!!

    @franciscorusso8062@franciscorusso806210 ай бұрын
    • Glad you liked it!

      @zoran-horvat@zoran-horvat10 ай бұрын
  • Thank you so much for another video. If I understending correctly the branching with switch pattern is mainly for benefit of consumer, say, for this example a view model, who can now more elegantly render appropiate html base on the type from branching instead of mess of if and else or I am mixing it with discriminated unions which a bit diffrent topic?

    @secury@secury10 ай бұрын
    • Pattern matching is the way to implement behavior in functional models, since a functional model is based on types and subtypes that have no virtual methods as in a corresponding object-oriented model. Any operation would then be implemented as a function which handles all su types of its argument type. A larger portion of behavior is then implemented via function composition, e.i. a chain of calls made to individual functions, each function alone doing one and only one transformation on its inputs. When these principles are followed consistently through the application, the overall impression will be that the code is shorter and more streamlined in what it does. You have also struck an important benefit from functional design when you mentioned UI. With behavior specified separately from the types, you can implement each piece of behavior at the place where it naturally belongs, for example in the UI if that is the interface logic.

      @zoran-horvat@zoran-horvat10 ай бұрын
  • I'm not sure I understand the problem. Is it that having a nullable datetime in the base class would be undesirable because you'd then have check if it's null for other logic such as hiding/showing the button?

    @1992jamo@1992jamo11 ай бұрын
    • Precisely! The entire software would be implemented in terns of null checks, with every method implementing two operations rather than one from its name. Worse yet, if you add one more nullable field, that becomes 3-4 operations per method. Add one more and each method in the code base will implement 5, 6,... 8 distinct behaviors. That quickly becomes hell.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • ​@@zoran-horvat Ah right. Interesting stuff that I have not thought about before since I have only coded in an OOP style. Without looking at this video, I'd make the datetime nullable and just have an expression-bodied member that does a null check. Then just have a method to set the date paid in the base class. For the UI I'd usually have have the UI list bound to an observablecollection and depending on the UI framework, have a converter to display the paid button. Honestly, the overhead for the types of applications I've worked on would be very little, but this video was very interesting to think about. For instance if I were to write a chess engine, this would be a deadly sin. Cheers for the video and for helping explain.

      @1992jamo@1992jamo10 ай бұрын
  • i think lesson about how to live with FP approach and EfCore would be great

    @vovcheable@vovcheable10 ай бұрын
    • I have that topic in the queue. There will be a video about that in the future.

      @zoran-horvat@zoran-horvat10 ай бұрын
  • Interesting video but I might have missed something. The idea behind the State pattern is to decouple the class from its state. Therefore shouldn't you have Invoice and an associated InvoiceState? So what you specialize is the state, not the invoice: PaidState, OutstandingState, ... The behavior of the invoice will then change as its state changes, as if the invoice changed its class.

    @vincentjacquet2927@vincentjacquet2927Ай бұрын
    • I don't think that is the purpose of that pattern. That _could be_ the purpose in some implementations if coupling to state causes design issues, but it would be an overstatement to say that should always be the case. I cannot remember my goals at the time of making this video, but I believe, from the outcome, that the state did not bother me too much, compared to state transitions.

      @zoran-horvat@zoran-horvatАй бұрын
  • I'm just spit balling here, but - when using EF Core - you would have to add these classes using a discriminator anyway, which in turn would create a discriminator column. I think it should, in theory at least, be possible to implicitly create a PaidInvoice from an Invoice, if you map the Dirscriminator as a property to your Invoice class and change it to "PaidInvoce". The change tracker would track that change and you can just call SaveChangesAsync. When you query that same entry again, you should get a PaidInvoice back from EF Core. Haven't tried it, but if it does, that would be very hackish I'd have to admit.

    @satyayuga0@satyayuga024 күн бұрын
    • Actually, no, and I think I have explained that in the video. You cannot persist state that is a function of time, because time passes even when your application does not work. All you can do is persist facts, and then reconstruct the state at a given time, which could be the current time, but does not have to be.

      @zoran-horvat@zoran-horvat24 күн бұрын
  • is it a naïve solution to just add a property? or was the example just for illustrative purposes?

    @istovall2624@istovall26247 ай бұрын
    • What would the property return?

      @zoran-horvat@zoran-horvat7 ай бұрын
  • But it also means that we don't need several classes to design the solution. We can have one Invoice class with a propery "PaymentStatus" - can be enum as well. Yes I know it is redudant with a PaidDate? but it also can give a clean info about the invoice. Also I would not rely on a system date about payment. Also we can introduce a new class Payment wich can have Invoice reference. So that class can be used for payments. Samo kažem. Also let not mix ORM entities with domain objects.

    @dxs1971@dxs19719 ай бұрын
    • Oh, no, no. You have mixed up unions (including derived classes) with tagged unions. The latter opens up all sorts of issues in code.

      @zoran-horvat@zoran-horvat9 ай бұрын
    • ​@@zoran-horvatVladimir Horikov has an interesting opinion about class pollution where you can have a simpler solution instead of a hierarchical set of classes, which increases in complexity with each new property that will be added.

      @dxs1971@dxs19719 ай бұрын
    • @@dxs1971 I agree with that opinion, but this is not a hierarchy of classes. A single level of inheritance is not a hierarchy.

      @zoran-horvat@zoran-horvat9 ай бұрын
  • Interesting! Although, I'm fairly sure that in 99% of cases, State patterns with OOP will not be used, but rather a pragmatic solution will be used. So in this case, the PaidOn? property will just be added to the base class to avoid the hassle. But it's an interesting approach nevertheless.

    @allinvanguard@allinvanguard11 ай бұрын
    • First, what you call pragmatic approach I call turning an object-oriented solution into a procedural one, with all the negative consequences - primarily moving operations to the consumer. Second, while you can evade the problem in a simple case, actual cases in practice are much more complex and such a workaround would not even be available.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat Yeah, I am with you on that one. I'm also leaning towards your immutable, functional approach when necessary. The OOP state pattern feels very clunky. But I was just stating what will probably happen in practice most of the time 😄

      @allinvanguard@allinvanguard11 ай бұрын
    • @@zoran-horvat Isn't the consumer also making the decision whether to create an instance of OutstandingInvoice or PaidInvoice? Or at the very least, calling the Paid method? Moreover, I would argue that under the OOP model what we have here is an invoice shifting from one state to another, not an invoice producing another invoice.

      @zevspitz8925@zevspitz892511 ай бұрын
    • Thats the problem with OO; doing it 'right' usually isn't worth it. The cost is so high you need a massive return to justify it. That just doesn't happen in most business software.

      @adambickford8720@adambickford872011 ай бұрын
    • @@zevspitz8925 The consumer does choose which class to instantiate. But don't forget that one such consumer is the ORM, which instantiates objects without knowing what it did and, on top of that, retains the reference to the object as a reference source for subsequent updates - updates that would never happen to that object! Regarding the last note, don't mix semantic with syntax. Semantically, the invoice changes. Syntactically, it does not, but rather causes a new object creation.

      @zoran-horvat@zoran-horvat11 ай бұрын
  • whats your opinion on language-ext lib?

    @benqbenq@benqbenq11 ай бұрын
    • My opinion is that it is changing the language in the way that the language evolution itself will not follow. It is obtrusive and oftentimes verbose, in places where there is no syntactic support for what it does. I am trying not to go against the language in programming. My code will utilize what the language is offering, but rarely more than that.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat thanks for the answer! if the language will not follow these concepts, do you mind switching to another language that has more fp features? i've seen your presentation about F# long time ago

      @benqbenq@benqbenq11 ай бұрын
    • @@benqbenq If you want a proper functional design, then F# should be the first choice. F# is a wonderful language, and it produces very short and effective code compared to C# counterpart. The advantage of C# is in the powerful combination of OOP and FP. I am just following the advancement of C# and trying to take the most out of it.

      @zoran-horvat@zoran-horvat11 ай бұрын
  • hvala zoki

    @greensporevalley@greensporevalley10 ай бұрын
  • Development fundamental, ERD and DFD as a Starting point before coding, will reduce some of the complexities related OOP and domain knowledge required.

    @rotteneggconcept@rotteneggconcept28 күн бұрын
    • In many business applications, E-R analysis is putting emphasis on the wrong aspect of the problem. A better solution is very often the one that puts more focus on rules and compositions than on entities, i.e. the one that better captures the dynamic nature of processes than their static representation. That is why ER analysis and design were largely abandoned about twenty years ago. Some derivatives appeared in the later years, but none could resolve the intrinsic problem and hence none of them caught up.

      @zoran-horvat@zoran-horvat28 күн бұрын
  • I'm confused. In another video, you argue against inheritance, yet in your example you use it to represent paid vs outstanding invoices. Shouldn't it use a compositional design instead? For instance, you could use a Paid optional which is unwrapped to retrieve whether it's been paid or not, and if so, retrieve its payment date? The only issue I see here is the complexity of querying a list of invoices based on its payment status or payment date, which requires unwrapping the optional for each of the invoices.

    @bobweiram6321@bobweiram63218 ай бұрын
    • I am not advocating against a single level of inheritance in any of my videos, i.e. inheritance over a single decision. How else could we create a composition if components are not polymorphic? However, you are right that this particular design would benefit from composition. Inheritance-based kinds of invoices would start showing their limitations soon after this stage.

      @zoran-horvat@zoran-horvat8 ай бұрын
  • Can you please simply explain why a nullable PaidOn property in a single Invoice type would be bad? I think it's a perfect model of a real-world invoice. Real-world invoice can be stamped with a red colored paid on this date and the null state of PaidOn property would mean the real-world invoice doesn't have the red stamp on it yet. What's wrong with this simple model? Why complicate it with multiple types like UnpaidInvoice and PaidInvoice?

    @XtroTheArctic@XtroTheArctic11 ай бұрын
    • As a matter of fact, I can simply explain the matter. Adding a nullable state, as in the best tradition of procedural design of the 1980s, would require all operations (dozens of them on an important model!) to contain a branching instruction and to implement two operations each, rather than just one. Quite often, consuming code would also use branching, rendering any subsequent evolution of the underlying model painful, if not impossible. That decision would add to cyclomatic complexity of dozens of methods, double the number of unit tests for them, but also help plant a few bugs here and there, bugs that would otherwise be impossible. The short answer is: That would be a flat design flaw. You could make it work, but it would be a flaw nonetheless. For the end, sincere advice: Learn programming beyond nullable state; learn languages that don't even have a null, at least not the one you can use in custom code. You will see a different programming paradigm that works much better than the programming of old.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat Thank you. It's a nice explanation from your point of view but personally I don't see a big problem with implementing branching code. For many decades, millions of applications and games were written with OOP and branching code and they served millions of people and made billions of dollars so I think the idea of banishing the branching code is not necessary even if it sounds and looks beautiful. I think the complexity created by having multiple different types for a single real-world object like an Invoice is much worse than having some branching code here and there. You are one of the rare people I highly respect on KZhead based on their programming (especially in C#) experience and I like your ideas and teachings but I guess we won't agree on this specific topic. Thank you very much!

      @XtroTheArctic@XtroTheArctic11 ай бұрын
    • @@XtroTheArctic I am consulting two companies right now which were thinking that same way. Millions in, and millions more. Until the cost of going forward started to burn twice that much, for the reasons I outlined above. Doing things that way can make the code work, but only until it grinds to a halt. Programmers usually solve the problem by quitting and finding a new job, but that doesn't make the company's software work beyond the point where expenses overgrow revenues.

      @zoran-horvat@zoran-horvat11 ай бұрын
  • This is coming from a beginner, but why have separate classes for each status of the object, instead of just having one class and have the status (paid, unpaid, etc) be a property?

    @k0v4c@k0v4c3 ай бұрын
    • In that case, it would be the caller's responsibility to interpret the meanings of these properties. With explicitly modeled types, the caller only needs to ensure that it possesses a certain interface. Any subsequent operation proposed by that interface must be safe and valid, so there is nothing the caller needs to do other than its own duties. That is the principle I often emphasize: If you have an object, then it's fine.

      @zoran-horvat@zoran-horvat3 ай бұрын
  • FP solution still changes the reference. If you have a List for example, and you want to change the type of one of its elements, without actually changing the list, you can't, even with FP. At least not with this approach.

    @davidc1179@davidc117910 ай бұрын
    • You wouldn't use the List type in an FP solution, but an ImmutableList. That solves the problem because all immutable collections are designed to support nondestructive mutation.

      @zoran-horvat@zoran-horvat10 ай бұрын
  • I feel like functional would be better suited to an event store than a relational database with mutable records. There is no such thing as a paid invoice, but there is an invoice and a paid event with a date. Data should be immutable all the way down.

    @7th_CAV_Trooper@7th_CAV_Trooper2 ай бұрын
    • Actually, there are both, but you are right that the payments are the principal source of information in such a system. The state of an invoice is calculated from payments, with part of that calculation typically being persisted as well (a.k.a. accounting).

      @zoran-horvat@zoran-horvat2 ай бұрын
  • So the reason you give for oop being unsuitable for this use case is that you would have to manage persistence more manually since ORMs can't track changes in type, but at the end the alternative presented still has the same problem. The functional approach is more succint yes, but it doesn't allow for anything else than what the oop approach already allows.

    @chiefxtrc@chiefxtrc3 ай бұрын
    • No, that is not what I explained. Persistence is not the reason but an in-place change of the runtime type which is not possible in any mainstream programming language, provided default modeling style based on mutable classes. Immutable classes that are modeled in a functional style do not have that limitation.

      @zoran-horvat@zoran-horvat3 ай бұрын
    • @@zoran-horvat how do they not have that limitation? You are still just returning new objects of a different type from the Paid function just like in the oop version Objects are still not changing their runtime type. Am I missing something?

      @chiefxtrc@chiefxtrc3 ай бұрын
    • @@chiefxtrc What limitation are you talking about? I am talking about changing the runtime type of an object. A functional style method returns an object and hence it can pick the runtime type of that object by its coding. An object-oriented style method mutates the current object and returns void. Hence in OOP the runtime type remains, and in FP it may change.

      @zoran-horvat@zoran-horvat3 ай бұрын
    • ​@@zoran-horvat Let me see if I understand. Say you call the Paid() extension method on an object of type OutstandingInvoice. A new object of type PaidInvoice is returned. This is an entirely new object, so the original object has not changed its type, it still lives in the heap as an OutstandingInvoice, along with the new PaidInvoice object. So it's two separate objects, none of which has ever changed its type. Is this not correct?

      @chiefxtrc@chiefxtrc3 ай бұрын
    • @@chiefxtrc Actually, in a functional design, you would leave the first object to the garbage collector because it is now outdated. There would be no references to it, but only to the new object. That is how we normally write any function in FP - as a chain of calls. Any value we may be starting with and any intermediate result is instantly forgotten about. All we keep is the last object in the chain.

      @zoran-horvat@zoran-horvat3 ай бұрын
  • In Smalltalk, where the term object-oriented was coined, every object has a method "become" which allows an object to become any other object of any class. I don't like OOP but it is not true that in OOP objects cannot become other objects.

    @jboss1073@jboss107311 ай бұрын
    • That crossed my mind when I was preparing this video, but I opted not to go that far (for the sake of the younger viewers). The problem is in how objects are managed in memory, which is driven by compiler considerations and whatnot, and so the situation in OO languages today is that they do not support this operation. Hence the State pattern as an attempt to work around the problem, but with added problems of its own.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat Thank you for the reply and I agree with your analysis.

      @jboss1073@jboss107311 ай бұрын
    • @@jboss1073 I forgot to thank you for mentioning that detail - I am sure that will help viewers see the problem better.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat You are welcome. I'm happy to remind people of Smalltalk.

      @jboss1073@jboss107311 ай бұрын
  • Is this really a restriction on oop? Is there a rule in oop that you cannot change the varient of an object? Discriminated unions would solve this problem while maintaining the oop style.

    @redcrafterlppa303@redcrafterlppa3038 ай бұрын
    • Discriminated unions are not the answer because they do not support dynamic dispatch - they are not object-oriented by definition. But your question about the limitation is a good one. The truth is that OOP alone does not forbid changing the runtime type of an object, but such an operation would impose a performance penalty on all other operations, due to the change in object layout. That is why statically typed languages are so widespread today, and the reason why this kind of an operation is not supported in any of them.

      @zoran-horvat@zoran-horvat8 ай бұрын
    • @@zoran-horvat one example is the rust option enum and it's take method. It returns the owned value and changes the runtime behavior of the source option by replacing it with the none varient. This is all statically dispatched only the branch which is taken is dependent on the runtime type of the object. Making it a cheaper more restricted form of polimorphism.

      @redcrafterlppa303@redcrafterlppa3038 ай бұрын
    • @@redcrafterlppa303 Rust enums and similar types in other languages are polymorphic, but do not support inheritance - you cannot extend them in another module. Therefore, they do not qualify as object-oriented. Also, functions that return the value do not qualify as type-changing entities when the original object remains. Someone has mentioned LISP as an example where (in CLOS) one can truly change the object's runtime type during its lifetime. But imagine the cost and constraints...

      @zoran-horvat@zoran-horvat8 ай бұрын
    • @@zoran-horvat you're right, they likely don't qualify as object oriented but they are type changing. fn take(&mut self) -> T { ... *self = Option::None; ... } The function is mutating the memory at it's "this" pointer changing the type varient of the object the function got called with. You can reassign this in rust functions which is not possible in java or c# and in many others.

      @redcrafterlppa303@redcrafterlppa3038 ай бұрын
  • Like i pre gledanja 😀

    @novalogic1265@novalogic126511 ай бұрын
  • Personally I'm a fan of functional programming. But I wonder, why do OOP people consider anemic domain model (which is what you show here) to be an antipattern?

    @chris-pee@chris-pee11 ай бұрын
    • Because their 'encapsulation' demands it. Mutation is a bad idea; unmanaged mutation is a slow-motion death sentence. OO thinks it works if you just define the boundaries right to make the mutation 'atomic' and 'well formed'. The problem is those boundaries change and are 'viral' so if you get it wrong, you are seriously screwed. FP simply decided "Mutation is *so bad it just can't be done right at scale. So don't"

      @adambickford8720@adambickford872011 ай бұрын
    • If you *really* drink the kool-aid everything can be solved by types. Imagine division: `public Integer divide(Integer numerator, NotZeroInteger denominator)` So now the types are used for correctness; its on the caller to make sure to pass valid objects and the implementation is very elgant/obvious. Well of course it is, you left all the actual work to every single caller of your lib you lazy scrub.

      @adambickford8720@adambickford872011 ай бұрын
    • @@adambickford8720 That makes sense. OOP often has a method on the model, that needs to set multiple fields ("atomic" operation) to have a valid state. What's the functional equivalent to that?

      @chris-pee@chris-pee11 ай бұрын
    • @@chris-pee In FP you have a function that returns a new struct in the proper state, likely copied from the initial state. Basically, how strings work. That means that the object isn't changing to other code referencing it (though that, in general, should be minimized via scoping either way) Some languages/apis make immutability easier, check out 'persistent collections' (has nothing to do with databases) for an example. Some languages have a 'with' or 'copy' function/keyword that allows you to update values w/o having to manually map the static ones.

      @adambickford8720@adambickford872011 ай бұрын
    • OOP people do not have mathematical or any sort of foundational principles that they are following. They are salespeople, not mathematicians like the people coming up with functional programming.

      @jboss1073@jboss107311 ай бұрын
  • Why is the ORM issue even a problem? Ideally you would have some DTO, not the actual ORM entity.

    @Wordsalad69420@Wordsalad6942011 ай бұрын
    • ORM already maintains a DTO for its own operation's sake, including the mapping from and to the object model. Adding one more DTO on top (a.k.a. the persistence model) is one more superfluous data reshaping operation, and that is the defeat of the ORM idea. That is far from ideal. Just consider how much code you would have to add to the code base to support that additional mapping.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • Ugh.. when I think about all the old code I had to manage that simply copied data from an Enity, to a DTO, to some Model object to finally be rendered.

      @CaptRespect@CaptRespect11 ай бұрын
    • @@zoran-horvat I always thought that the service layer (i.e. the thing consuming the ORM/repository) is the thing that owns the DTO and does the mapping of the entity coming out of the repository. This is how all of our code is written. The presentation layer then consumes the service layer's DTO. The presentation layer does not re-map the DTO to its own DTO. Why is the ORM/Repository layer providing its own DTO beneficial? Have we we been doing this wrong all along? 😅

      @Wordsalad69420@Wordsalad6942010 ай бұрын
    • @@Wordsalad69420 In complex cases, implementing a separate persistence model is the only way, so you were probably right - unfortunately. I say unfortunately because it was the promise of ORMs to do the mapping. Don't forget that an ORM is already performing a mapping inside, so implementing another model between that and the domain model is not adding a persistence model - it is adding a second persistence model. The root cause for this need, in EF Core in particular, is that it doesn't support multi-column and multi-table mappings. All it can do is map a single column per expression. The upside is that you can still do pretty complex things without a persistence model,provided that you can split the domain model into isolated submodels as in DDD. What I am seeing recently is that EF Core is evolving in the direction of supporting more and more DDD modeling out of the box. That might render persistence models unneeded in most cases, just as we always wanted ORMs to be.

      @zoran-horvat@zoran-horvat10 ай бұрын
  • 11:10 there is no necessary in the pattern matching expression, you may just call a paid invoice constructor transfering source invoice properties as arguments

    @lucifer-look-@lucifer-look-3 ай бұрын
    • That is precisely the issue. Once you choose to pass all the values to the new object instead of just the modified ones, maintaining that list becomes your eternal duty. Don't you see that the with expression is resilient to future changes in the target class, including its constructor?

      @zoran-horvat@zoran-horvat3 ай бұрын
    • ​@@zoran-horvat I don't get it, look it your code -- you already do call PaidInvoice constructor explicitly. All I am saying is to remove unnecessary code around. Anyway, if you will have a new type of invoice, let it be "PaimentFailureInvoice", you would have to rewrite most of pattern matching expressions in all of your project, where Invoice descendants are mentioned, to add a new logic for the new invoice type.

      @lucifer-look-@lucifer-look-3 ай бұрын
    • @@lucifer-look- Adding a new type to the discriminated union breaks all existing functions in exactly the same way as adding a new method to the base class breaks all the derived class. A discriminated union is orthogonal to a class hierarchy. Each does best what the other cannot do. The reason why functional programming based on discriminated unions is getting so widespread in business applications development today is that in modeling business domains we rarely add a new representation to a set, but we keep adding new functions on top of it. That is precisely where discriminated unions cope very well and traditional classes suffer.

      @zoran-horvat@zoran-horvat3 ай бұрын
    • @@zoran-horvat I could provide a solution with no necessary to change any existing code. But, you are skipping another point -- why do we need that pattern matching expression there, if we already have an explicit constructor call.

      @lucifer-look-@lucifer-look-3 ай бұрын
    • @@lucifer-look- Because the alternative is branching with ifs. Once you get to having more than one condition, ifs turn into a nightmare. The entire functional programming is built on pattern matching expressions. You can question it, of course, but apart from the most trivial examples, you will be at a loss. And, of course, you are constantly evading the with expression which is meant to keep you away from knowing the constructor's signature.

      @zoran-horvat@zoran-horvat3 ай бұрын
  • Just add a Settlement class into the Invoice which contains the data needed to describe the settlement. Settlement can be related to Invoice in an ORM. I think you're over complicating the problem.

    @edgeofsanitysevensix@edgeofsanitysevensix5 ай бұрын
    • Settlement would exhibit all the traits outlined in the video, but yes, that would be the way to go. Though, not the way to remove complexity, as I said. The complexity will remain.

      @zoran-horvat@zoran-horvat5 ай бұрын
    • @@zoran-horvat I'm not sure why you would have Invoice, OutstandingInvoice and PaidInvoice when there is only one entity and its state is either paid or not paid. I'm on board with functional programming but returning a new Invoice object with a mutated state seems redundant. You can just update the state on the original object and pass that back to an ORM. It literally is one state or the other. In an event based system having non mutable objects is invaluable of course.

      @edgeofsanitysevensix@edgeofsanitysevensix5 ай бұрын
    • @@edgeofsanitysevensix It is not paid or not paid, to begin with. Nor can you just update the original object and pass it back to the ORM, because the object exposes behavior that depends on the status it represents, and that is not just one class. Could it be that you forgot three out of five states and all the methods when you thought you saw a simpler model?

      @zoran-horvat@zoran-horvat5 ай бұрын
  • Hmm. I think the original problem was a bit of contrived. I would argue the data model was already wrong. Payments is something I would prefer to solve with an event-sourced approach - every data structure immutable. Of course there may be "dynamic" data structures that are just aggregates of multiple such events. But a mutable invoice? No.

    @cyrusol@cyrusolАй бұрын
    • Why do you dismiss traditional object-oriented modeling in favor of event sourcing, like event sourcing is suitable for every problem?

      @zoran-horvat@zoran-horvatАй бұрын
    • @@zoran-horvat I haven't dismissed it. I dismissed this example as a suitable one.

      @cyrusol@cyrusolАй бұрын
    • @@cyrusol Does that mean that the conclusion stays, in a different example? Because the video is about the conclusion, not about the example.

      @zoran-horvat@zoran-horvatАй бұрын
    • @@zoran-horvat Sure. You're suggesting to use immutable data structures yourself and that the ORM has to be designed in a way to cope with that. Immutable data structures are good. Relying on an object's identity for functionality could even be considered a design smell, breaking encapsulation in the strictest sense.

      @cyrusol@cyrusolАй бұрын
    • @@cyrusol What do you suggest instead of object identities?

      @zoran-horvat@zoran-horvatАй бұрын
  • class Invoice { prop State: Paid/Oustanding} => problem solved. The original design is bad.. You used the type of classes to representing the state of the Invoice, so it is awkward to mutate this information. If you created a normal property "State" for Invoice then everything becomes normal OOP

    @duongphuhiep@duongphuhiep23 сағат бұрын
    • The entire problem moves into the state class, then, and repeats its manifestation in the way depicted in the video. You solved nothing.

      @zoran-horvat@zoran-horvat23 сағат бұрын
  • Or you could have a look at CLOS. Of course that one's about generic functions. Still OOP..

    @drequena@drequena10 ай бұрын
    • Anyone with a degree not only "had a look" at CLOS, but also passed an exam. You could avoid sounding elitist and mention JavaScript instead. Then we could talk about why that is not the right answer (nor is that with CLOS).

      @zoran-horvat@zoran-horvat10 ай бұрын
    • @@zoran-horvat JavaScript! Keep going… You must be the only one with a degree around here. I guess that didn’t sound elitist 😂 For the the non-degree holding crowd, CLOS allows for actual object reclassing

      @drequena@drequena10 ай бұрын
    • ​@@drequena So, to conclude - your answer is wrong because, while CLOS is OO, OO is not CLOS. CLOS is making implementation-specific assumptions that are not, and cannot be, part of the definition of OOP. Take Rust as an example. Rust cannot implement runtime type changing due to its extreme performance-optimized view of objects. And it is still very much OO.

      @zoran-horvat@zoran-horvat10 ай бұрын
    • @@zoran-horvat so at the end of the day, you agree with me. The problem you state is not OO specific but more of a limitation of MOST OO environments, as implemented. Such thing is demonstrated by the mere existence of an OO system on which that limitation does not exist. Why such belligerence? I too tend to favor funcional programming over object orientation this days, just for different reasons

      @drequena@drequena10 ай бұрын
  • I don't understand why people think using inheritance as crazy is what OOP is about. There is nothing wrong with adding a paidDate attribute to the class.

    @f1945@f194511 ай бұрын
    • To the base class? Well, if that is the idea, then let me tell you what is wrong with that - the operations would then.move to the consumer rather than staying in the producer. When done consistently, that ruins the entire design.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat I am familiar with consumer/producer in the context of concurrency design. How are you defining them in this use case?

      @nicknelson5372@nicknelson537211 ай бұрын
    • @@nicknelson5372 I used it in terms of a feature, which has a producing and a consuming end. A consumer should not vary, i.e. branch, depending on conditions in the producer - that is indicative of procedural design, which quickly becomes unmaintainable. In a proper design, the variation is managed at the producing end. The consumer receives a unified subject to work on. That is a polymorphic object in OOP or an injected function in FP, but either way the question "what that object/function is" is resolved before control is passed to the consumer. You can come to the same conclusion by following different design methods. For instance, observing the SRP leads to the same ideas. Once popular CRC cards would enforce this model, too.

      @zoran-horvat@zoran-horvat11 ай бұрын
    • @@zoran-horvat why would you move that operation outside the invoice class? In the video, you say that you have to remove that attribute because of the single responsibility principle. And first of all that is a SOLID principle, not all OOP programmers follow those principles all the time. and second, even if you do, composition must always be chosen over inheritance. In that case, you can add a Pay object as an attribute.

      @f1945@f194511 ай бұрын
    • @@f1945 You can add Pay object as a polymorphic attribute. Everything said in the video now applies to Pay, rather than Invoice.

      @zoran-horvat@zoran-horvat11 ай бұрын
  • Having a separate class for each logical subtype of a type is an extraordinarily poor approach which will lead to brittle and difficult to maintain code.

    @ericblankenburg@ericblankenburg8 ай бұрын
    • What do you suggest as an alternative?

      @zoran-horvat@zoran-horvat8 ай бұрын
    • It depends on the circumstances. I operate on the KISS principal -- Keep It Simple Stupid. If you have a lot of subtypes that have minor differences, just collapse them into the supertype. Use a "type" property to manage what is valid and needed by each instance. If your subtypes have very large differences, you might have modeled the system incorrectly because these might not really be subtypes in the business domain. For example, I literally could make everything a subtype of "entity", but that wouldn't be very helpful. Again, it all depends on the circumstances. The goal of good software design is that someone else should be able to come along in the future, look at your code, easily understand it, and change it. Elegant design, overbearing "architecture", and object-oriented purity is often at odds with this goal. My two cents.

      @ericblankenburg@ericblankenburg8 ай бұрын
    • @@ericblankenburg You are advocating so-called poor man's polymorphism, then. The irreparable issue with that approach is that domain-related logic is implemented at the calling place, rather than at the place which is serving the objects. That leads to enormous code duplication in large systems, but also creates tons of nonmaintainable code because implementation is misplaced by design. That approach remains valid in simple cases - that explains the success of Go. But one should not attempt it in enterprise-grade software development. Fun-fact: object-oriented programming was invented (as a measure of coding discipline at first) within procedural languages more than half a century ago to deal with consequences of what you have just proposed today.

      @zoran-horvat@zoran-horvat8 ай бұрын
    • @@zoran-horvat - I am not advocating duplicating any code, nor am I advocating implementing domain-related logic in the caller, whatever that means. I am advocating KISS -- keep it simple stupid. I've been architecting, designing, and building software products and solutions for over 30 years, for some of the biggest companies on the planet, including Microsoft and IBM. I've seen it all, from big balls of mud which had individual classes with thousands of lines of code to over-architected systems that had 9 "layers" of code and multiple data-transformations between the UI and the database. The thing that actually works is KISS. The goal should be to keep the system simple, and easy to understand, which makes it easier to maintain and enhance.

      @ericblankenburg@ericblankenburg8 ай бұрын
    • @@ericblankenburg Poor OO design doesn't make procedural design any more powerful than it is. Also, such 9-layer designs are addressing problems of a great scale where procedural design doesn't even apply. It is not comparable. This video is showcasing a stripped-down part of the accounting part of a large software. 1% of it. Would you dare use tagged types in financial calculations of a versatile business? On top of my head I know of at least three requirements where that approach would fail miserably on code from the demo.

      @zoran-horvat@zoran-horvat8 ай бұрын
  • Sorry, but the OOP example is horrendous. PaidInvoice needs to be constructed with the Invoice object. Essentially just wrapping the InvoiceObject to perform the "paid" functionality. Then the implementation is clean without needing to make modification to parent/child classes. You could even modify it to allow multiple payments to the Invoice without touching any other Invoice code. Shouldn't cause any issues with ORM as well as long as you're using a dataMapper pattern instead of activeRecord. You just need to do the same thing in the dataMapper class to add the new functionality.

    @darylphuah@darylphuah11 ай бұрын
    • What you are referring to is to use object composition to represent variable state. What you have forgot to mention is that the component would also be polymorphic, and so everything said in the video moves to the payment-related component contained in the invoice.

      @zoran-horvat@zoran-horvat11 ай бұрын
  • I'd say the only thing is good software.

    @parlor3115@parlor311510 ай бұрын
    • I have witnessed many times that programmers who are incapable of writing good OO code are equally if not more incapacitated when writing functional code. What's your story?

      @zoran-horvat@zoran-horvat10 ай бұрын
  • Work properly? Write fast and clean code? That is why you can have F#

    @MarcosVMSoares@MarcosVMSoares11 ай бұрын
  • Bad take

    @felipedidio4698@felipedidio469811 ай бұрын
KZhead