The Square-Rectangle Problem

  Рет қаралды 9,056

Christopher Okhravi

Christopher Okhravi

Күн бұрын

The infamous Square-Rectangle Problem is a great way to understand why Liskov's Substitution Principle is important. Do you know the answer to the problem?
00:00 The Problem
01:08 Defining Rectangle
01:35 Using Rectangle
02:16 Defining Square
02:37 Using Square
03:27 Overriding methods
04:39 Violating the super type
09:09 Question
09:27 Takeaway

Пікірлер: 172
@mattiasandersson9825
@mattiasandersson9825 2 ай бұрын
Ok, so here it is. After beeing active in the profession of Software Engineering for 31 years and spreading the gospels of SOLID design priciples for allmost as long, you still make me want to go back to school, more in particular, attending your classes. You are a tribute to Uncle Bob & Barbara Liskov, love it!
@ChristopherOkhravi
@ChristopherOkhravi Ай бұрын
Thank you for the very kind words. 🙏
@praveenr3396
@praveenr3396 2 ай бұрын
Welcome back Chris. Please continue posting videos. I like your way of explaining the stuff
@MegaCuerdas
@MegaCuerdas 2 ай бұрын
Your energy and passion are contagious, and your knowledge invaluable, thank you very much!
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
🙏
@Tsunami14
@Tsunami14 Ай бұрын
Pretty much took the words out of my mouth. I only discovered his channel today, and I'm totally digging the vibe.
@abdoulkarimbalde2802
@abdoulkarimbalde2802 2 ай бұрын
Hello Christopher Okhravi, it's Abdoul Karim BALDE, from Guinea, West Africa. Very happy to watch your videos again, it's longtime. I like your videos, you explain very well, keep continue please.
@Flutterdev6391
@Flutterdev6391 2 ай бұрын
Men you'r teaching way is really awesome ur examples left bluePrint in our mind for forever.
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Glad to hear that. 🙏
@Rajmanov
@Rajmanov 2 ай бұрын
Welcome back, my friend! It's fantastic to see you again.
@MrAbhinavbaijal
@MrAbhinavbaijal 2 ай бұрын
Nice to see you back. I have learned a lot from them. Especially the design pattern series is one of my favourites. Thank you 🙏
@droam129
@droam129 2 ай бұрын
Great video as always! In addition to “if I change the width of a rectangle, I would expect it to not affect the height”, the other side of the coin is “if I have a rectangle, I would expect to be able to set the width to say 3 and the height to say 5.” But if the rectangle is secretly a square, you’ll end up with a 5x5 rectangle rather than a 3x5 rectangle.
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Exactly! You get it 😊😊
@karlmehltretter2677
@karlmehltretter2677 14 күн бұрын
Thanks so much for this ! I had previously implemented a "PrimeNumber" class in Java.. Now I'm deriving a "Two" class, a "Three" class, and a "Five" classs and so on. I'm gonna watch the next video when I'm done ;)
@verfran
@verfran 2 ай бұрын
My take away: Use base class only to hold common behavior, anything else would break LSP. I always had difficulty convincing developers to follow LSP, but this example makes a great argument/point. Thank you
@bogdanb904
@bogdanb904 Ай бұрын
I think this is exactly what's explained in his previous video. Only use sub-types for behavior changes, not for data changes.
@SunsetNova
@SunsetNova 2 ай бұрын
Glad to see you back
@MichaelKire
@MichaelKire 2 ай бұрын
Good to have you back 😊
@sanjaycs89
@sanjaycs89 2 ай бұрын
Glad to see you posting again 😊
@TheBorsooq
@TheBorsooq 2 ай бұрын
I love the content :) Great to have you back!
@johnny5gr
@johnny5gr 2 ай бұрын
Nicely put. We want more! Thank you!
@sanjubaba1339
@sanjubaba1339 2 ай бұрын
It’s good to see you back after long time. Was missing your good content .
@siddharthkothari007
@siddharthkothari007 2 ай бұрын
Thanks Chris for nicely putting the topic. Waiting for more cool stuff.
@babaiiseuphoric
@babaiiseuphoric 2 ай бұрын
You are back with a bang! Glad to see you back😊
@Luis-Torres
@Luis-Torres 2 ай бұрын
So good to have you back Chris! I hope you've been well :)
@unofficialshubham9026
@unofficialshubham9026 2 ай бұрын
thanks for starting your amazing video again
@guidoerfen7944
@guidoerfen7944 2 ай бұрын
Best explaination I came across so far.
@GooogleGoglee
@GooogleGoglee 2 ай бұрын
You are really good man! I appreciate your videos and clear explanations... Subscribed ❤ 👍🏻
@MrAymenmatador
@MrAymenmatador 2 ай бұрын
woow amazing, I have never grasped the LSP easily as it was explained in this video.
@juriddu
@juriddu 2 ай бұрын
welcome back master.
@josda1000
@josda1000 2 ай бұрын
mannnn it's good to see you again! And yes I love using behaviors (strategy) for this kind of situation. I really can't stand conditionals anymore, and factories/builders truly keep that to a minimum.
@faduregis
@faduregis 2 ай бұрын
There is always something new to learn in programming, thank you Christopher for answering this challenging problem. Btw longtime!
@brtk7
@brtk7 2 ай бұрын
That example you presented helps me understand what Liskov Substitution Principles really is. Thanks 🙏 and as you suggested the better refine contract could be an interface with only readonly properties that both classes would implement.
@olivermeyer3963
@olivermeyer3963 2 ай бұрын
What would the name of this interface be?
@olivermeyer3963
@olivermeyer3963 2 ай бұрын
ISomewhatKindOfSquaReact maybe just kidding
@brtk7
@brtk7 2 ай бұрын
@@olivermeyer3963 you know you need to remember to not overthinking here, it’s just an exercise, name is here secondary
@juanibuscaglia3239
@juanibuscaglia3239 2 ай бұрын
The way I see it, just because something has been defined in some way in some specific field, that doesn't mean it's a good idea to implement it that way. Not only is a square a special case of a rectangle, but also of a parallelogram and trapezoid. And I've been taught to avoid multiple inheritance. Besides, in mathematics you usually have different ways of defining the same thing (there's a definition of rectangles defined by their internal angles and axes of symmetry). My point is that one shouldn't feel too restricted by formal definitions and should instead try to approach it in a way that makes sense for the particular requirement you're trying to model. A simple web app to calculate the area of different shapes is going to have a very different implementation of a rectangle-square as a 3D CAD program, and so on. One potential approach is that of Maya's API (a 3D DCC package), with objects and function sets that you attach to those objects to make them behave in specific ways. You could have a rectangle (or parallelogram, even) as a basic shape, and then you can attach a square function set to the shape. There's no inheritance, so there's no contract that gets violated. Say your basic shape is the rectangle, var myrect = new Rectangle() is the only possibility. You want to have a square, you create the function set var fn_set = new SquareFnSet(myrect), fn_set.set_side_length(10) will mutate your rectangle as needed, perhaps squaring it by default if it's oblong) The downside (which I don't even think is a downside) is that given a shape you wouldn't know what specific shape it is. But you could either have some general functions in the base class (is_square), or through some other way. Of course, this is completely overkill for a simple program, but it's probably how I would model such relationship.
@olivierbouzonnie8025
@olivierbouzonnie8025 2 ай бұрын
Also very glad to see you back. Your series on "head first : design pattern" was a huge help while i was trying to become a professionnal programmer (which I am now). Do you have plan to do more of them, or tackle architecture design patterns in the futur ?
@maorof
@maorof Ай бұрын
great video
@othmanothman4530
@othmanothman4530 2 ай бұрын
love it man glad u r back
@RezaulKarim-cq5ft
@RezaulKarim-cq5ft 2 ай бұрын
If the properties of square and rectangle are immutable and can only be set through constructor, the things left are the potential methods of both class. For example finding out the area.To find out the area of a rectangle , we multiply height and width, again to find out the area of square, we only have to do height^2 or width^2. Since both height and width of a square is equal, it can be achieved with the rectangle's Area method that does height*width to find out the area. Now the thing is, what if the user does not use the Area method of the superclass and instead does the operation on their own. So, if we treat rectangle as super class, the user will end up doing height * width to find out the area. it also covers square. But if we treat square as super class, and rectangle as sub class, then user might end up doing Height*Height or Width*width to find out the area which does not satisfy the condition of the area of a rectangle. So, in my opinion if all the fields are immutable, treating rectangle as super type is enough.
@alexstone691
@alexstone691 2 ай бұрын
Truly love your energy in these videos, makes me think this through If im understanding it correctly its about expectations/contract, the code is hidden in this case as you get the super type Rectangle but the subtype breaks the expectations/contract by changing the behaviour when setting width/height
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Sounds like you’re getting it 😊
@AussieRossco
@AussieRossco 2 ай бұрын
Nice. You made it so concrete. The principle is normally hand waved away as pedantic.
@karanbopche
@karanbopche 2 ай бұрын
Thank you. 😊
@Mihes22
@Mihes22 2 ай бұрын
Thank you 🙏
@faouzibenmabrouk4538
@faouzibenmabrouk4538 2 ай бұрын
Welcome back !
@MMMM-vc5oi
@MMMM-vc5oi 2 ай бұрын
Exactly correct, immutable class is the solution. Your approach and method to explain problems is awesome. Another way is to define just Rectangle class with boolean field IsSquare inside
@karlmehltretter2677
@karlmehltretter2677 14 күн бұрын
I make all my classes immutable now so that I never will have any issues and bugs anymore.
@rahullama395
@rahullama395 2 ай бұрын
Legend is back.
@DoChDev
@DoChDev 2 ай бұрын
The problem here is that getting width and height should be a different interface/contract than setting them. For a square you wouldn't actually set width or height, you'd set the side length. Getting width or height however would be no issue, both would return the side length.
@francescoleto2823
@francescoleto2823 Ай бұрын
You are amazing. Maybe a Shape superclass can solve the LSP?
@AhmedAli-ld6en
@AhmedAli-ld6en 2 ай бұрын
strong stuff hope to see more in the future
@Michaeljamieson10
@Michaeljamieson10 2 ай бұрын
awesome stuff
@arminium56
@arminium56 2 ай бұрын
Awesome 🎉
@bunglegrind1
@bunglegrind1 9 күн бұрын
An enlightening reading is smalltak objects and design by chaimond liu, chapter 17. The problem with class inheritance is that it allows method overrides, which in practice violate LSP. If the instances are read-only you don't override get/set and LSP is satisfied
@pathakvivek7865
@pathakvivek7865 2 ай бұрын
Awesome.
@praneetrane2993
@praneetrane2993 2 ай бұрын
Welcome back
@Templarfreak
@Templarfreak Ай бұрын
before watching the full video: i think one issue with understanding squares and rectangles in this way is that a square is actually a regular polygon but a rectangle is not. you can define *all* regular polygons with just one length value that is applied to every side, but you cant do that with a rectangle. however, you *can* define a rectangle with 2 different values, and you also still need two different values to define any polygon, the length of the side and the number of sides. for a rectangle, you need the length and the height instead of the number of sides. and, interestingly enough, ellipses also are defined with 2 values, and circles can be defined like an ellipse but where the two points of the ellipse are just at the same position. this means that all of these shapes can be defined with only 2 values, its just that how those 2 values are interpreted varies from polygon to rectangle to ellipse to circle.
@personalJoke
@personalJoke 2 ай бұрын
Why Square, Equilateral Triangle and Circle can share the same common ancestor? I love the opportunity to talk about these topics.... I haven't written code in soooo many years. Here are my three implementations: 1. An abstract class called shape having a property called length, and another one called sides. From there I implemented only the behaviours Square, EquilateralTriangle, and Circle. 2. A class, and three methods: AsEquilateral, AsSquare, AsCircle. 3. A class Shape accepting functions as parameters to calculate the area. Circle doesn't use Sides in any of those implementations. So, the big question is: Are these implementations breaking the Liskov's Substitution Principle? BTW, thanks for bringing back to me the enthusiasm and joy of writing code again!
@corlaez
@corlaez Ай бұрын
Bro made the best argument for why inheritance sucks without even trying. A function isSquare() and a function changeBothWidth andHeight (if you really want to go that far) would get rid of all issues.
@snail8720
@snail8720 2 ай бұрын
Good video! I made a similar example for immutable types and indeed the issue does not persist, because the initialization of the object's properties are set, the square can be treated exactly the same as a rectangle. The guarantee that height and width are equal is enforced by the constructor, and past that you don't need to think about it. The next obvious question is how do we resolve this conundrum while allowing the objects to be mutable?
@DrSpooglemon
@DrSpooglemon 2 ай бұрын
If the Rectangle class has a method makeSquare(int side) as well as setters for width and height then the object can be either a rectangle or a square depending on usage. It could also have a method isSquare() to determine whether the sides are of the same length.
@olivierbernard2733
@olivierbernard2733 Ай бұрын
At beginning you Speak like a guru ^^
@ChristopherOkhravi
@ChristopherOkhravi Ай бұрын
Haha. Not sure if this is a good thing or a bad thing 😊 Nevertheless, thank you for watching 😊
@akashacharya2813
@akashacharya2813 2 ай бұрын
Hey Chritopher, Your explanation about SOLID principles and OOP Design Pattern made me a better software engineer, do you have plans to make cloud design patterns principles playlist. Would really appreciate if you do that, the way you explain concepts are awesome, and it gets ingrained in the brain.
@Google_Censored_Commenter
@Google_Censored_Commenter 2 ай бұрын
The way I like to deal with these problems is using what I call the "1 assumption rule". Whenever you have some definable object with values or properties you want to set, you're only allowed to program in 1 assumption per input. So in this example, if I was writing a width *input* for a rectangle object, and I wanted the height to be assumed, that's it. I have no more assumptions left. I can't assume to know the color of the rectangle, of what kind of subtype it is, such as a square. I can assume nothing else about it, if I want to assume its height. I only get 1 assumption. If I want more assumptions, I'll have to use more user inputs. This really helps when you're dealing with 3 or 4 inputs and you're unsure if the assumptions you're trying to program in are really feasible or not, or if you're just a bad programmer. Carefully count how many assumptions you're dealing with, and you'll know the answer. I don't know if there's any mathematical validity to the rule, but it's worked for me so far.
@r0bertdenir0
@r0bertdenir0 Ай бұрын
Is this "1 assumption rule" enforceable in code? How would I write my code to restrict the future me or team mates from violating this?
@Google_Censored_Commenter
@Google_Censored_Commenter Ай бұрын
@@r0bertdenir0 depends what you mean by enforceable. You could just add a comment next to the input / assumption line in your code, isn't that sufficient? Assumptions are very broad, so if you were looking for a way to automatically detect how many you've made, don't think that's feasible. Programs can't read your mind and know exactly what your goal is. I should also mention that "inputs" count as any kind of certain information. Not just what the user defines. Like suppose you have some object with a velocity input X. Strictly speaking, because movement entails a change in position, the information about its positional coordinate along the direction you applied a velocity, is another "input". It isn't an assumption, it's a derived mathematical certainty. (If there's nothing else that might change its speed, like a collision with a wall can be ensured) So it can be tricky to know exactly how many inputs you're dealing with. You have to ask yourself "could this thing be otherwise in any realistic scenario?" if no, it's probably a safe input to rely an assumption on.
@krozaine
@krozaine 2 ай бұрын
As is rightly captured, the definition of a Rectangle matters a lot! Statement: A rectangle never said that changing the width will NOT change its height. If all the Rectangle said was that - It's a 4 sided polygon with all inner angles as 90 degrees, all the formulae for area, diagonal length and perimeter would hold true for a Square implementation of a Rectangle. The client should not get spooked about height getting changed when width is changed - the client shouldn't care as long as the initial idea of what it means to be a Rectangle isn't violated. I guess the same case can be built for Rhombus and Square also. Very interesting video and a good revision for LSP!
@silberwolfSR71
@silberwolfSR71 2 ай бұрын
I agree with the general sentiment of your comment - what really matters is the contract. I think a big part of the issue is the contract of rectangle as defined by the problem. An example of a contract that could make sense: Rectangle { float height() float width() void scaleBy(float factor) } We're now defining another invariant of a rectangle - its aspect ratio. This allows us to make both rectangle and square be mutable, but without violating the contract. But you can do even cooler things if you make both immutable. For example, you can still do single-side scaling without breaking contracts: Rectangle { float height() float width() Rectangle scaleBy(float factor) Rectangle scaleX(float factor) Rectangle scaleY(float factor) } Now the "mutated" object no longer has to be a square (and thus is freed from having to adhere to the invariants of a square). Scaling a square along only one of the axes can simply return a rectangle. (Note that it would be enough to write the implementations of these methods in Rectangle and simply inherit them as is for Square) You could even add methods like "Rectangle withHeight(float newHeight)" if you want more control than that.
@lima1877
@lima1877 2 ай бұрын
You’re back? Wow
@kamalam29
@kamalam29 2 ай бұрын
@Christopher, you can also teach us History or Economics or Art or Maths but request you to not hold yourself to make videos. We love listening you and engage with you. Thank you for being awesome teacher.
@IrizarryBrandon
@IrizarryBrandon Ай бұрын
One thought: this reminded me of the "don't use polymorphism unless" video where you explain that subtyping based on variations in _data_ is a bad idea. This looks like a prime example of that, amirite?
@ApprendreSansNecessite
@ApprendreSansNecessite 2 ай бұрын
That's one of the many reasons why FP > OOP for domain modeling. Mainy people say that FP is unintuitive but people intuitively expect immutability when they think structurally about anything.
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Interesting point 😊 Thanks for sharing.
@rafaelocariz1384
@rafaelocariz1384 2 ай бұрын
Honestly, I would have a base type named Shape, then a subtype Quads or Polygon4Sides and then Rectangle, Square, Trapezoids and Kites for example.
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Such a hierarchy would probably not violate LSP 😊 Thanks for the thoughts.
@TheAd021
@TheAd021 2 ай бұрын
I would prefer composition over inheritance. Square is a wrapper (or an adapter) for a Rectangle and has only the public methods setSide, getSide
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
I agree that for more complex cases (ie where the classes have more code) that is probably the best solution 😊
@jf3518
@jf3518 2 ай бұрын
That is why I like composition over inheritance.
@RiazShageer
@RiazShageer 2 ай бұрын
Holy crap this was a good video....
@davithov
@davithov 2 ай бұрын
Then, how to solve the issue described in case of mutable members? Maybe avoiding inheritance altogether (or any form of subtyping) could be a viable approach? BTW, although I am aware that you have videos covering SOLID principles, but collecting them into one comprehensive resource with examples would be highly valuable. In any resource known to me I like your explanations the most. Thank you very much for your effort!
@AnotherFancyUser
@AnotherFancyUser 2 ай бұрын
I hate LSP, it is one of those principles in SOLID that usually you don't mess around a lot, you usually try to not violate SRP, OCP and DI.
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Thanks for the perspective. You mean that it’s uncommon to accidentally violate LSP?
@brtk7
@brtk7 2 ай бұрын
In my opinion it is not uncommon as Christopher described in the video. You simply can make your code more error prone, buggy and be unaware that you break lsp. It’s bad design which isn’t something that makes your code impossible to run. And sometimes you do it on purpose to somehow glue everything together. Example could be c# array implements ICollection but when called (ICollection) Add on array it throws exception.
@silberwolfSR71
@silberwolfSR71 2 ай бұрын
09:09 If both types are immutable, they are able to establish their invariants during construction (or fail) and guarantee that they will hold throughout the lifetime of the object. There is still a danger of violating LSP though, if we aren't careful with the contracts of the two types and the relationship between them. For instance, having square be the supertype can lead to problems like e.g. some other component that computes the area of a square using the width^2 formula, and getting a wrong result when doing the same for a rectangle. So rectangle should definitely be the supertype. If we do that, make both types immutable and use the common mathematical conceptions of what it means to be a square, respectively rectangle, then I think we're in the clear. The _is-a_ relationship holds, we just need to define square and rectangle correctly. I feel like Liskov is the most critical part of SOLID: all the other guidelines are nice and help you keep your code in order; if you violate them you'll have a hard time when you want to change something. If you violate Liskov, your code is broken. By definition, assumptions are violated. If anything at all depends on those assumptions, you have a bug. Relating back to your previous video on data-invariant polymorphism, one could argue that there is no need for the concept of a square, right? Granted, the relationship is ancestral rather than fraternal in this case, but the same arguments apply, I would think. (BTW I disagree with that position; I like having squares even if they're just custom rectangles)
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Wow. Such an elegant comment. Thank you very much for sharing this. We are in agreement on all accounts 😊 Especially about LSP being the most important principle.
@Mig440
@Mig440 2 ай бұрын
Or you could forgo inheritance wholesale and just never abstract, although that would be more procedural oriented atleast you can be very specific in you methods and only accept the specific types you want to operate on. Maybe that is the reason why real world software projects are more procedural since atleast it does not pretend to be universalizable/abstractable but concrete everytime since more often than not, the invariants of any concept is unclear at the inception of the project and probably remains so until someone finds out what those invariants are, but then it is too late and risky to reify those constraints and so it never gets done🤣
@AshleyPillay99
@AshleyPillay99 Ай бұрын
In this example, the problem does seem to arise from the types being mutable. When some code get a Rectangle object & wants to mutate that object, it implicitly must have a strong concept of what a Rectangle is. If both the Square & Rectangle types are immutable, then only the creator of the object instances have a strong idea of what those instances are, but that is fine because they are creating the objects. Some other code that is handed a Rectangle can only inspect the object but not change it. This only requires weak knowledge of a Rectangle as a thing having Width & Height. It's fine for such code to retrieve that Width==Height, there's nothing special about that - it's dealing with a Rectangle it doesn't know/care about Squares. Perhaps this is why Squares are special case Rectangles in math? Because in math they're implicitly immutable?
@josephlagrange9531
@josephlagrange9531 2 ай бұрын
Hi, Chris, your best friend here!
@neozes
@neozes 2 ай бұрын
Maybe I missed the point, but a rectangles contract is that is has 4 right angles, not that it has a width and height. So the square would never violate the rectangle contract. It looks like a problem of contract definition in this example. But I think I get a underlying idea.
@enricomariadeangelis2130
@enricomariadeangelis2130 2 ай бұрын
My takeaways: 1. Embrace immutably whenever possible, 2. Prefer concept-based duck typing. Imho the two mutable classes should not have an inheritance relationship, but be mutually convertible and ==comparable to each other, with the conversion from rectangle to square asserting if the sides are different. The client API should not take either class, but any class having members to get/set sides, compute area, and so on.
@MatrixQ
@MatrixQ 2 ай бұрын
I'd look at it from a data perspective. To define a square, I need 1 value: the length of the sides. To define a rectangle, I need an additional value. Therefore, I could inherit from square and add that value as a new property.
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
If Square does not expose separate properties for Width and Height that could work without violating Liskovs Substitution Principle. Thanks for the comment. 😊
@uscorporation
@uscorporation 2 ай бұрын
Good video! I still have a question. I understood it is not good to have a Square as a parent of a Rectangle if a Square has both Width and Height. But what if Square has only one property A, and Rectangle is inherited and extended by property B? Then when we are calculating the square of Square we have A*A, and overridden function in the Rectangle as A*B
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Great question. This would probably work for the "setter" but probably not for the "getter". If Rectangle is a subtype of Square, how would you override the method "getSide" in Rectangle? More generally, the question we should ask is: Can the subtype inherit or override all the public methods of its super type without behaving in an unexpected manner, if someone expects an object that behaves like the supertype? Thank you for the question 😊
@AfriandiHaryanto
@AfriandiHaryanto Ай бұрын
I think, if we want to keep the relation (square is a rectangle) then width - height should be immutable and passed from constructor. The violation happen when we have to deal with mutability down the line. That's my opinion, CMIIW.
@Wickerman1989
@Wickerman1989 2 ай бұрын
Great example. Is this also a violation of the Liskov-substitution principle? I don't expect an elaborate answer :)
@Wickerman1989
@Wickerman1989 2 ай бұрын
Uh oh, okay I made it to the end, case closed :D
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
😊😊 But good on you man for spotting that before I said it. 👏
@ashishkarn9283
@ashishkarn9283 2 ай бұрын
wow. This is crystal clear. However, about the solution. I wonder if making both of the classes would solve the real problem or is just a work-around ? I believe that it would be a way to avoid the problem and it is not a perfect solution and ,so, either square or rectangle should never be considered a subtype of the other. @Christopher Okhravi sir, could you answer this?
@andreipacurariu2013
@andreipacurariu2013 2 ай бұрын
This is a well-known problem, indeed. However, the way I rationalize that a Rectangle can legitimately be viewed as a subtype of Square is by pointing out that the Liskov substitution principle is not in fact violated here unless the client breaks encapsulation, such as by calculating the area of the rectangle outside of the class instead of using a provided method or property such as Area. It can be argued that a 3x3 rectangle is a legitimate rectangle. There is no contract that a rectangle MUST have a width different from its height. While a rectangle CAN have a different width and height, there is no contract that it MUST, so a NxN rectangle is a perfectly valid rectangle. Therefore, the apparent Liskov substitution violation can only occur if the client assumes that the width and height of the rectangle MUST be different. For example, if the client multiplies the width of the rectangle by 2 and ASSUMES that this multiplies the area of the rectangle by 2 then the Liskov substitution principle would appear to be violated. However, this is only because the client broke encapsulation and calculated the area of the rectangle outside the class itself rather than using the GetArea() method or Area property. If, however, the client would not make such assumption, and instead would multiply the width by 2, or set it to whatever value they chose, and then use the provided Area property, then there would be no bug introduced in the code. This might violate the expectation and assumption around how the area evolves in relation to altering the width, so I can see why this would look like a Liskov substitution violation, however, if encapsulation is not violated, there should be no unexpected effects. Rectangle rect = new Square(); // 1x1 rect.Width = 3; // 3x3 Console.WriteLine(rect.Area); // 9 --> While we might expect it to be 3 this expectation is based on violating encapsulation and performing the calculation in our minds, outside of the provided Area property. In other words, while we assumed we're dealing with a 3x1 rectangle, we were in fact dealing with a 3x3 rectangle. This is still a perfectly valid rectangle which perfectly honors the contract that Area = Width x Height. The only thing being violated here is our assumption that we were dealing with a 3x1 rectangle when in fact we were dealing with a 3x3 rectangle. So, the way I see it, the contract is stricter for the square, in that its width MUST equal its height but there is no requirement or contract for a rectangle that its width MUST NOT equal its height. In other words, the contract for a Rectangle is not that its width and height MUST be different but merely that they CAN be different. Simply put, any square is a legitimate rectangle. So, as long as we don't materialize our expectations and assumptions in a way which violate encapsulation, then there should be no unexpected side-effects. However, I can see how a case might be made that Square and Rectangle are in fact both subtypes of Parallelogram which is in itself a subtype of Quadrilateral which is in itself a subtype of a Polygon. Thereby, a Square and a Rectangle are not interchangeable but instead the immediate superclass of both is a Parallelogram.
@user-ev9jg6ts6e
@user-ev9jg6ts6e 2 ай бұрын
I think it's wrong to treat inheritance as "is-a" relationship. It's clear that square is a rectangle, but that doesn't work. Instead inheritance is kind of "behaves like" relationship. In that sense square is a rectangle but "does not behave like" rectangle from width and height point of view :)
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Hmm. 🤔Would you care to elaborate? Inheritance does indeed establish a subtype relationship (in virtually all OO languages) such that objects of the subtype can be used when objects of the super type is expected. This means that establish what, for most intents and purposes, can be thought of as an is-a relationship.
@user-ev9jg6ts6e
@user-ev9jg6ts6e 2 ай бұрын
@@ChristopherOkhraviOk. Is square a rectangle?) It definitely is. So if we think of inheritance as "is-a" relationship, then our square should be able to inherit from a rectangle, but you showed in the video that it violates LSP. So a contradiction here in the sense that square IS A rectangle geometrically , inheritance is "IS-A" relationship, square cannot be inherited from a rectangle. So my suggestion is that inheritance is "behaves like" relationship. I hope it's a bit clearer now.
@susseduud
@susseduud 2 ай бұрын
Lets go :D!
@user-ev9jg6ts6e
@user-ev9jg6ts6e 2 ай бұрын
Hi Chris. Highly appreciate. Please Don't disapperar again. By the way, I think you should start writing a book on object-oriented software design :)
@zeocamo
@zeocamo 2 ай бұрын
i never got this problem as i don't use inheritance in my code, it is a GREAT a idea for planning on a whiteboard, but when you need to change the program, it is the worst idea in the world. and in this example that could be the Rectangle become a heart form, and now you need to change 20% of the program.
@r1konTheAutomator
@r1konTheAutomator 2 ай бұрын
Havent watched the video yet, stopped after intro - the way I would do it out of these two is rectangle being a sub type of square. Having a default of all equal sides and angles is basically the definition of a sensible default. Lets see what happens 😅
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Thank you very much for sharing this! Makes for a much more interesting journey 😊 Let me know if you have questions along the way.
@karlmehltretter2677
@karlmehltretter2677 14 күн бұрын
If I derive a class "SlowRectangle" from "Rectangle" that adds 1 second artificial delay to every method, will I break LSP ? It depends on if there is a guaranteed execution time in the specification, that is now broken.
@AnalogDude_
@AnalogDude_ 2 ай бұрын
lol, good video.
@thefattysplace
@thefattysplace Ай бұрын
What if you use reflection to see what type the compiler is expecting in the sub class square, and only update the height if it is expecting square and not rectangle? I appreciate this pretty isn't possible from system.reflection, but if it was, it would solve the problem!
@guilhermecampos8313
@guilhermecampos8313 2 ай бұрын
Doesn't that mean that method overriding always violate LSP?
@VetaliySl
@VetaliySl 2 ай бұрын
The guy is a little crazy but definitely not boring 😄
@bovineox1111
@bovineox1111 Ай бұрын
I’m not sure I agree this violates Liskov. Just because the super type has width and height, where is it written that changing either cannot change the other? It may even be that conceptually this is exactly right. The thing that confuses this is the relationship we attach because of the names of the classes. If they were called Parent and child and the props are Number1 and Number2. Is it still incorrect that changing either changes the other? Isn’t his about inversion of control? We have an expectation that the two values are independent but why? Again it comes down to naming - if the parent type were shape with a width and height we wouldn’t be saying this is wrong. You can definitely substitute squares for rectangles and this compiles and also functions fine - why does it not work? Nothing in the pure contracts discussed here enforces the two properties to he independent, it’s only our minds putting an expectation on Rectangles and squares. So what if changing the width also changes the height?
@InforSpirit
@InforSpirit 2 ай бұрын
This one is direct violation of last video... Since I pondered this some time I come to conlusion that there can be only Rectangle-class and instance of square. If later someone want to transform it to rectangle, it is Clients problem in that point. (immutable typing if really needed)
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Thank you for the comment. Great question. Just because it is possible to create a subtype relationship (based on behavioral variations) that violates LSP doesn’t necessarily mean that ALL subtype relationships (with behavioral variation) will violate LSP. See what I mean?
@InforSpirit
@InforSpirit 2 ай бұрын
@@ChristopherOkhravi Not really sure that I Understand. I come clearly from practical standpoint so maybe you should define subtype by rules of stasis theory. if subtype include instances and SubClasses then I understand all. Academic question: Can you proof that LSP will give benefit in every use cases? I bet it is as hard to proof as halting problem. Because now you have subjective parameter of 'benefit' How you can universally define benefit? You can't because it is derivate of relation of infinite combinations. Circle-Ellipsis proofs that in atleast in some cases adhearing to LSP make everything more difficullt (less benefit) In my experience interface contracts (methods) is above all other rules. Client gives information to method and it returns type that is defined in contract. If it is dimensions, Rectangle returns x = n, y = n If you have no interface, there could not be any problems, but that point it is useless structure. Does Circle or any geometric shape has any behaverion in first place? IS this just primacy bias (thing that we learn first has biased weighting in namespace) whispering lies? You have condition to think circles and ellipsis as different group from kindergarden to today. You had the calculation power to define Area of circle, but circle If you rethink had no function by themself. And thats why fundamentally they should be immutable type. (In real application you just return some abstract vector cloud and doesn't care what Client do for that)
@Mig440
@Mig440 2 ай бұрын
Or maybe.. just maybe introduced union types in dotnet (youre writing c# code anyway in the examples)
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
That would be cool 😊
@janailtongoncalvesdesouza4160
@janailtongoncalvesdesouza4160 2 ай бұрын
Posted the question to chatgpt and he gave me an answer with square extending rectangle... your job is safe lol
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
😆 ❤️
@dominikvonlavante6113
@dominikvonlavante6113 2 ай бұрын
Liskov Substitution principle: always go from abstract to concrete
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Interesting rule of thumb. Thanks. 🙏
@AFPinerosG
@AFPinerosG 2 ай бұрын
I honestly don't see the issue. It just depends on the contract you define. If your contract is that the width and height of a rectangle is provides during initialization and then can't be changed, this isn't an issue. Rectangle r = new Square(3); That's it. Then you can get the area and other things from the rectangle contract. So, it's not a problem with the nature of the objects but with the nature of the contract.
@adambickford8720
@adambickford8720 2 ай бұрын
While both have the same properties, they do not have the same behavior and mutability makes this problematic. Imagine instead both are simple immutable types/structs/records. You could then have a service that has a `withWidth(Rectangle | Square shape, int w)` but no matter what it's passed you're getting back a `Rectangle` because that's the only contract it can provably honor. While a `withSize(Rectangle | Square shape, int size)` _could_ give you back either, a square is more useful/accurate and it's now up to the caller to decide how fungible the 2 types are. (I'm assuming a discriminated union, but there's other ways to model it)
@julians.2597
@julians.2597 2 ай бұрын
The solution is composition
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
Yes, but we must still understand LSP because composition without subtyping is less powerful than composition with subtyping 😊😊
@loam
@loam 19 күн бұрын
I don't see violation there, because subtype modifies further the original behaviour, and it's normal In this case it's not some completely unexpected behaviour for rectangle. Update: And if you instantiate the square into base rectangle object, you know there's underlying square object, and when you use it, it might implement its operations accordingly to itself, and this is normal, it is what polymorphism for. If you have list of rectangles, some of them being just rectangles, and some of them being Squares, you know for squares they'll do their own logic, when you set values... Ok, ok, now I see 😂 So, if you set Width and Height for them to some different values, and expect all of them to have those set to specific different values, that will not happen. Ok, may be some violation is having place here 😂 I guess this is related to Liskov substitution principle. For instance, I saw examples in C#, where subtype doesn't implement method its base class has, because in the context of subtype that method was not applicable, and throws NotImplemented exception. In this case I see violation, because it's kinda like you can't say the subtype is a base type, it's only partially.
@alexandernava9275
@alexandernava9275 2 ай бұрын
Still watching. I would just make a state check of square, as square is a state of a rectangle. That way the behavior is separate from the state.
@shahaffrsshahaffrs5190
@shahaffrsshahaffrs5190 Ай бұрын
Solved: IPolygon -> IFourSidedPolygon -> IRightAnglePolygon -> Square, Rectangle 😂
@klamberext
@klamberext 2 ай бұрын
I think we got that squared
@ChristopherOkhravi
@ChristopherOkhravi 2 ай бұрын
🤣
@bovineox1111
@bovineox1111 Ай бұрын
You done’t have both classes. The Squareness is a property of the rectangle and rectangles would include the ability to check squareness and set squareness.
Liskov's Substitution Principle | SOLID Design Principles (ep 1 part 1)
16:08
Christopher Okhravi
Рет қаралды 156 М.
Understanding Covariance and Contravariance
13:31
Christopher Okhravi
Рет қаралды 10 М.
О, сосисочки! (Или корейская уличная еда?)
00:32
Кушать Хочу
Рет қаралды 6 МЛН
Follow @karina-kola please 🙏🥺
00:21
Andrey Grechka
Рет қаралды 22 МЛН
I PEELED OFF THE CARDBOARD WATERMELON!#asmr
00:56
HAYATAKU はやたく
Рет қаралды 36 МЛН
The Only Time You Should Use Polymorphism
13:55
Christopher Okhravi
Рет қаралды 85 М.
8 Wastes of Lean (for Software Developers)
10:54
Christopher Okhravi
Рет қаралды 8 М.
Liskov Substitution Principle (SOLID)
20:16
Christopher Okhravi
Рет қаралды 7 М.
`const` was a mistake
31:50
Theo - t3․gg
Рет қаралды 75 М.
Factory Method Pattern - Design Patterns (ep 4)
27:21
Christopher Okhravi
Рет қаралды 530 М.
Depend on Abstractions not Concretions (Framework)
11:56
Christopher Okhravi
Рет қаралды 14 М.
3 Ideas on Refactoring by Martin Fowler
5:50
Christopher Okhravi
Рет қаралды 18 М.
О, сосисочки! (Или корейская уличная еда?)
00:32
Кушать Хочу
Рет қаралды 6 МЛН