Empty superclass for Collection of derived classes

Basically, what I would like to obtain is a way to iterate through ONE list, and call methods specific to the interface the objects in the collection implement.

In my Java project, it would result in something like this:

//GameComponent would be my empty abstract class  
HashSet<GameComponent> components = new HashSet<GameComponent>(); 
components.add(/* instance of a class that extends GameComponent and implements Drawable || Updatable */);  

for (Drawable d : components)
    d.draw();

for (Updatable u : components)
    u.update();

My friends suggested me to just make two separate lists, but that means some objects will be in both lists.
I also think that it makes sense (abstraction-wise) to have one list of GameComponents and iterate through that one list.
But I also don’t like having an empty class to inherit from, only for categorisation.

So, the question is: does my approach make sense? If so, is it elegant? If not, what pattern could I follow?

3

A couple of thoughts:

  1. It’s better if game components don’t know how to draw themselves. A class should have only one reason to change; here your components may change if you need to change what they do in-game, and they could change if you need to change how they’re drawn as well.

  2. For this scenario the most straightforward solution is use instanceof. It’s not the most general solution but it’s the simplest thing that’ll solve this particular problem.

    for (GameComponent component : gameComponents) {
        if (component instanceof Drawable) {
            ((Drawable) component).draw();
        }
        if (component instanceof Updatable) {
            ((Updatable) component).update();
        }
    }
    
  3. If you have a finite set of GameComponent types, what you want is a sum type, which mainstream OOP languages don’t provide native support for but you can kind of emulate through the Visitor Pattern. (There’s an alternative way of implementing them but it won’t be elegant in Java; it works best with named arguments and lambda expressions.) This is a more general solution to taking decisions based on the particular kind of GameComponent because it gives you full access to the component type.

It would look something like this:

    public interface ComponentVisitor {
         void visit(Foo foo);
         void visit(Bar bar);
         void visit(Baz baz);
    }

    public abstract class GameComponent {
        // prevent subclassing outside this scope
        private GameComponent() {}

        public abstract void accept(ComponentVisitor visitor);

        public static final class Foo extends GameComponent implements Drawable, Updatable {
            // implement ...

            public void accept(Componentvisitor visitor) {
                visitor.visit(this);
            }
        }

        public static final class Bar extends GameComponent implements Updatable {
            // implement ...

            public void accept(Componentvisitor visitor) {
                visitor.visit(this);
            }
        }

        public static final class Baz extends GameComponent {
            // implement ...

            public void accept(Componentvisitor visitor) {
                visitor.visit(this);
            }
        }
    }

Now you can take component-specific decisions like this:

    for (GameComponent component : gameComponents) {
        component.accept(new ComponentVisitor() {
            public void visit(Foo foo) {
                // do something Foo-specific here
            }

            public void visit(Bar bar) {
                // do something Bar-specific here
            }

            public void visit(Baz baz) {
                // do something Baz-specific here
            }
        });
    }

This works because any component needs to implement accept, and for visit(this) to compile the Visitor needs to have a method that accepts that particular kind of component. It’s still possible to implement a component such that accept doesn’t call visit(this), so we safeguard against that by using a private constructor. That prevents outside subclassing while allowing subclassing of inner classes (because inner classes have access to private members of their enclosing class.) The end result is a finite set of components and an exhaustive way to discriminate between them (you can’t forget to handle one of them because the Visitor implementation wouldn’t compile.)

5

A few things to consider:

  • Why is GameComponent empty? If there are any methods/properties that are shared between all children of GameComponent it wouldn’t have to be an empty abstract base class.

  • Does GameComponent have to be a class? If you’re not defining any default behaviour or values in it, you could just as well make it an interface.

An empty interface is not neater/more elegant per se, but using interfaces to ‘tag’ an object as being of a certain type let’s it keep its ability to extend another class.

1

I see no problems with this.

For example, when I login I’ll end up with a login result:

public abstract class LoginResult { ... }

When I login, I might have three scenarios:

  • Login failed as password doesn’t match
  • Login succeeded
  • Login succeeded, but account is banned

To accomplish this, I would then introduce three classes that inherit from the base:

public class SuccessfulLoginResult : LoginResult 
{
   // May contain a string with a welcome message
}

One for when login fails:

public class FailedLoginResult : LoginResult
{
    // String containing information as to why the login has failed and integer property indicating how many login attempts are left
}

One for when account is banned:

public class AccountSuspendedLoginResult : LoginResult
{
   // String containing information as to why account is suspended and potentially a date time field indicating when account will be activated again
}

I would then use it in the following way (C#):

var loginResult = _accounts.Login(“Username”, “Password”);

if (loginResult is SuccessfuLoginResult)
{
   // Do something
}
else if (loginResult is FailedLoginResult)
{
   // Do something else
}

Base class can stay empty and I see no problems with that.

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