Is it good to have an interface plenty of methods which belong to different concepts, just to preserve the Liskov’s Principle?

I’m currently studying a course based on Software Design and I had a discussion in class with my professor and some classmates about a problem represented by the next scenario:

Scenario

Imagine we have a graphic application which lets us plan the interior design of our future house using a perspective from top. We are able
to add or remove some elements like furniture and change their
position, but obviously, we can’t move the walls of the house because
it was already built.

Solution 1

To solve this problem, some of my classmates proposed a solution that could be expressed using the following UML diagram:

As you can see, they agreed on the use of a common interface called “Drawable” which represents the graphical objects displayed on the UI. The general class “App” manages a list of Drawables, and each of them has a set of methods. This interface is implemented by different classes such as Furniture, Wall or Window. The thing is that a Furniture object could be moved, but not the Walls nor the Windows. So, a Furniture would implement the ‘move’ method defined in Drawable. In contrast, Wall and Window simply will write it empty.

At this point, other classmates and I complained about this decission because the design does not require to fulfill with the constraints (like moving or not Drawable objects, depending on their nature). This way, the design will allow some new objects could be moved, although they probably shouldn’t be. However, the professor said this design is good because it’s flexible and transparent for the App class, because this class doesn’t know what kind of instance is managing.

Extended case

In addition, we could think on an extreme case where we add several methods to the Drawable interface, such as ‘sell’, ‘open’ and ‘lend’. Using the same approach described above, we will have an interface which could be able to do anything, like Superman. Therefore, I believe this is a bad design solution due to we are mixing behaviors which belong to different concepts (Movable, Sellable, Openable, and so on). Also, we are allowing a Wall object could implement the ‘sell’ method, which completely does not make sense… but my professor still insisted on his point of view and he didn’t see the problem.

Solution 2 (based on the extended case)

Other people suggested we could add those interfaces (Movable, Sellable, Openable) and each of them would inherit from the Drawable interface. That way:

  • A Wall would implement Drawable directly
  • A Furniture would implement Movable and Sellable
  • A Window would implement just Openable

The next diagram summarizes this approach:

[Edit: The Drawable’s move method should be removed, this is a mistake because it is now placed into the Movable interface]

Questions and Doubts

Finally, once you can figure out the problem, could you help me and answer these questions, please?

  1. Aren’t we breaking the Single Responsibility Principle with this super-mega-god-interface?

  2. Are both approaches valid against the Liskov’s Substitution Principle? I think it’s broken in the first case because the post-conditions are broken due to some methods don’t do anything. Anyway, in the second approach I’m not sure as well because of the interfaces inheritance tree

  3. Maybe, another alternative could be the definition of basic Drawable objects (with ‘click’ and ‘draw’ methods). Thus, we may take advantage of some kind of mechanism like the Decorator pattern in order to add behaviors dynamically. But how?

  4. If we use a language without interfaces such as JavaScript or Python, how can we deal with this problem?

6

Aren’t we breaking the Single Responsibility Principle with this super-mega-god-interface?

Not exactly, because the interface isn’t doing anything. It has no responsibilities.

You are breaking Liskov Substitution Principle and Interface Segregation Principle though.

LSP because you’re writing methods that do nothing, just to make it fit the interface (ie. What is true of IDrawable — that you can move it — is not true of Wall).

ISP because you’ll have situations like a trade method, which should have access to Buy and Sell methods, but will also have access to Draw.

Are both approaches valid against the Liskov’s Substitution Principle? I think it’s broken in the first case because the post-conditions are broken due to some methods don’t do anything. Anyway, in the second approach I’m not sure as well because of the interfaces inheritance tree

It’s just as broken in the second case as the first.

Edit: On second thoughts, it isn’t. But it is still broken. It doesn’t seem automatically true that everything that could have a Move method will also have a Draw method. It might be true in your case but, if you later find yourself wanting to pass an object with a Move method into a method that receives an IMovable, you’re going to have to implement Draw on that object, even if you don’t need it.

And it gains you nothing over a third solution …

Maybe, another alternative could be the definition of basic Drawable objects (with ‘click’ and ‘draw’ methods). Thus, we may take advantage of some kind of mechanism like the Decorator pattern in order to add behaviors dynamically. But how?

The best alternative is to segregate your interfaces fully. You can still have objects with lots of methods (but Dependency-Inversion Principle and Single Responsibility Principle dictate you must push the logic down into services), but they only have to implement the interfaces they need. Wall shouldn’t implement IMovable, if it can’t be moved.

public class Wall : IDrawable
public class Furniture : IDrawable, IMovable, ISellable
public class Window : IDrawable, IOpenable

Nothing here dictates that any of those interfaces must derive from another.

public interface IDrawable
{
    void Draw(Canvas canvas);
}

public interface IMovable
{
    void MoveTo(int x, int y);
}

etc

You should then manage your list of IDrawables, which you can only be sure implement Draw(), and cast to see if other methods are available.

For example,

ITradable tradable = drawable as ITradable;
if (tradable != null)
{
    tradable.Trade(buyer, seller);
}

You’re going to have to do the above in your option 2 anyway, so why tie everything to IDrawable?

If we use a language without interfaces such as JavaScript or Python, how can we deal with this problem?

OO languages tend to support at least one of three things:

  • Interfaces (use … interfaces)
  • Multiple inheritance (use abstract classes)
  • Dynamic types (use duck-typing; this is where Python comes in)

Prototypical languages, such as Javascript, support … well, prototypes, which allow for a slightly modified version of duck-typing (in prototypes, you generally ask if a method exists before calling it).

Other paradigms generally handle these issues in their own way.

14

  1. Solution 1 violates not only Liskov’s Substitution Principle but it also violates the Interface Segregation Principle and the Single Responsability Principle. Solution 2 is the correct approach (see correction at the bottom).

  2. Same as above

  3. You can do that and solution 2 at the same time, they are not exclusive.

  4. You should use non-OO solutions and patterns.

CORRECTION: Solution 2 is also wrong. Sellable and Movable must not inherit from Drawable. They should be separate and unrelated interface.

4

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật