1

I'm trying to write a simple game and I really want to finally create something that would be "programmatically correct". I stuck with a problem like this: I have class

public abstract class ProjectileLogic<T> where T : ProjectileInfo{
    protected T _projectileInfo;
    public abstract void OnInitialize();
    public abstract void OnHitTerrain(Vector2 hitDirection);  //hitting the floor should give Vector2.down, etc.
    public abstract void OnHitEnemy();
    public abstract void Destroy();
}

I'm using generics, because inheriting classes need different objects inheriting from ProjectileInfo class. ProjectileInfo contains data like damage, initial speed etc.

The problem appears, when spawner creates the projectile and calls it's "OnInitialize".

var projectile = spawner.CreateProjectile(ProjectileType.Pellet);
projectile.GetComponent<ProjectileLogic<...what exactly?...>>().OnInitialize();

GetComponent returns the component attatched do game object "projectile" of a given type.

I thought, I could create interface IProjectileLogic containing all methods of ProjectileLogic and then:

public class ProjectileLogic<T> : IProjectileLogic where T : ProjectileInfo{
    protected T _projectileInfo;
}

and then spawner:

var projectile = spawner.CreateProjectile(ProjectileType.Pellet);
projectile.GetComponentByInterface<IProjectileLogic>().OnInitialize();

But I really don't know if it wouldn't be misuse of interfaces and bad practice used only because "it helps in this single case".

user3387666
  • 111
  • 1
  • What exactly is `ProjectileInfo` and why is its behaviour abstracted to another class and the projectile does not have the behaviour inside it? What benefit do you think it brings in your case? – Andy Jul 14 '16 at 11:44
  • ProjectileInfo contains informations about projectile statistics. PelletInfo for example contains just damage and speed, but GrenadeInfo contains also fuse time. Data in ProjectileInfo is not part of ProjectileLogic, because statistics in it depends on character statisctics, so character create ProjectileInfo and puts it in the projectile. In fact, i should write var projectile = spawner.CreateProjectile(projectile_info_given_by_character); – user3387666 Jul 14 '16 at 12:06
  • It might make it a little clearer that an implementation may change in the future. Other than that, [YAGNI](https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it). – Andy Aug 13 '16 at 16:29
  • Game programming typically has stuff that makes most writers about things that are "programmatically correct" have high blood pressure. People that are writing about "programmatically correct" are almost always writing about line-of-business apps. What you are doing is going to bring you a great deal of anguish for no benefit. – whatsisname Aug 13 '16 at 17:05

3 Answers3

1

The purpose of having an interface is so that you can declare a variable to be of type of that interface, and then actually populate that variable with any class that implements the interface, and all the functions that are declared in the interface will be available. That is, the interface should declare all the functions that you want to use in cases where it could be populated with several different classes.

I don't know just what you're doing with your "projectile" class, but to invent a simple example, suppose I had a system with objects for several different kinds of people: customers, employees, and stockholders. All of these types of people have names and addresses. I have a function that formats addresses and prints them on envelopes. It makes sense to write this function once and use it for all three types of people rather than write it three times. So I create an interface, Person. I then make objects Customer, Employee, and Stockholder implement Person.

At this point, Person requires just two functions: GetName and GetAddress. It almost certainly would not include all the functions in all three of the implementing classes. There are likely things I do with employees that I do not do with customers, like calculate the amount of their paycheck and enroll them in the company insurance plan. There are things I do for stockholders that I do not do for employees, like email them invitations to the annual shareholders meeting. Etc.

There are cases where an interface would have all the functions that are in the implementing classes. Maybe the only difference between the implementations is HOW we do something, and what needs to be done.

But I don't think making the interface have every function that's in the implementing classes should be any sort of goal. If it happens that way, fine. If not, so what?

Jay
  • 2,657
  • 1
  • 14
  • 11
0

In terms of the SOLID principle, do not force implementation on classes that do not need it. Generics are nice but tend to lead to premature refactoring and pigeon hole you into using the expected type. If you wish to refactor your interfaces your generics will give you a lot of headaches.

I would suggest separating your interfaces. Here is great example I find myself referring to over and over again as the years pass.

http://www.codeproject.com/Articles/703634/SOLID-architecture-principles-using-simple-Csharp

0

Short answer: no. Doing it "right" can get you into trouble if you don't know why rule you're following is there, and therefore whether it applies in your case. You're working from the SOLID principles, of which you are trying to follow D: Dependency Inversion Principle.

In this case, you have a class and an interface, but you don't know why you're separating them, so you don't know what belongs in which. That is, you have interface code but no conceptual interface: no contract that the interface implies. So you end up writing all your method signatures twice, which makes the code harder to read and change, all for nothing.

I don't know anything about Unity 3D, but it looks like it's using Dependency Injection, and might require an interface. If so, you're doing what you need to. Otherwise ditch the interface and just use a class.

The goal is not to get perfect code, but to be able to move toward better code. Your class is the prototype to help you design the interface you need, and it may be all you ever need.

That means you should be concerned less about writing an interface, and more about being able to convert to an interface in the future.

David Leppik
  • 211
  • 2
  • 5
  • Side note: OO languages often get in the way of the SOLID principles. Good code should be easy to read and easy to modify. The SOLID principles make code easy to modify, but if the language gets in your way, applying them sometimes make things worse. So the right answer changes if you switch languages. – David Leppik Nov 11 '16 at 19:46