Search Unity

WHY did I AVOID interfaces!? Discuss cool things u can do with them here.

Discussion in 'General Discussion' started by Slyder, Jul 22, 2015.

  1. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Exactly. There are however advantages to using base classes as well sometimes though. Let's say all of your objects "Use" the same way... then you can define the functionality for Use in the base class. However, you could also mark it virtual and Item, Door and Car can all "Use" the same way (from the base class) but Another Player can override Use and implement it an entirely different way. In fact, you can do this fun bit as well:

    Code (csharp):
    1.  
    2. public class AnotherPlayer : Actor
    3. {
    4.    private int _someValue;
    5.    public override void Use() : base.Use()
    6.    {
    7.         _someValue--;
    8.    }
    9. }
    10.  
    Now the _someValue field gets decremented and Use is still called on the base class as well. You can also call base.Use() from within your method if you want to control when it gets called.
     
  2. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Yes, the difference is that your second interface doesn't actually have to define the methods from the first. Example:

    Code (csharp):
    1.  
    2. public interface IInventoryItem
    3. {
    4.     //You can define properties in interfaces as well
    5.     bool Inspected {get; set;}
    6.  
    7.     void ViewItem();
    8. }
    9.  
    10. public interface IQuestItem : IInventoryItem
    11. {
    12.      int QuestId {get; set;}
    13. }
    14.  
    Now if you implement IQuestItem you have to define Inspected and ViewItem as well:

    Code (csharp):
    1.  
    2. public class SecretKey : IQuestItem
    3. {
    4.     public bool Inspected { get; set; }
    5.     public int QuestId {get; set;}
    6.     public void ViewItem()
    7.     {
    8.           //Fancy stuff here
    9.     }
    10. }
    11.  
    There is another important note and distinction. Anything defined in the interface has to be public. In a base class you can mark things as "protected" so that only inheriting classes can use them. So in the case of a Property in an interface, if you want the getter to be public and the setter to be private, then you cannot define the setter in your interface:

    Code (csharp):
    1.  
    2. public interface IPrivateSetter
    3. {
    4.     int SomeValue { get; }
    5. }
    6.  
    Now when you implement IPrivateSetter, the SomeValue has to have a public "get" but can have a private "set". That's ok though because in this case you're only going to be setting it from within your class anyway so there's no reason it needs to be part of the interface contract.
     
    CheekySparrow78 and Kiwasi like this.
  3. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    In my implementation, Car, Item, Player, and Door would be completely unrelated (other than MonoBehaviour) so an interface makes sense.

    I did think the whole Pawn, Actor, etc... system was neat in UE4, but not sure it would be worthwhile to reproduce it in Unity.
     
    Last edited: Jul 23, 2015
  4. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    I'm actually not allowing objects to call methods on each other at all.

    They have to send messages to other objects notifying them of the collision, the objects then have custom structures consisting of collections of references to what I call "events" which can be broadcasts or enabling and disabling scripts. The broadcast feature is the one in particular that is so fascinating... because I can have a script attached to the door that causes it to rotate, and a script attached to a window that causes it to slide up, and they can only be triggered through the exact method of Door Open or WindowOpen, but the object itself handles how to react to external events

    Basically, we know a couple things... namely, that all objects can collide with other objects. Whether or not they choose to do anything in that case is entirely up to them.

    That's what I've done, I have arrays of custom event structures, they include ways of specifying what objects they hit (by class or ID) and what to do in the case of such a collision. What I plan on doing is making each collision event its own component, that way I can copy and paste collision event/reaction components from one object to another and multi-edit more reliably.

    It works really well and I can get a lot done pretty quickly.

    Previously I used strings for the classes and ID's but in the future it will all be enums, because I did waste some time with typos... bleh.

    Any time you write code that's not reusable, you guarantee duplication to some degree.

    I believe each object is an atomic entity, responsible for itself, and if each object isn't fully self contained, meaning handling its own logic, events, and ultimately its own death you will have issues where you waste time undoing some weird dependency that made sense at the time. And that even the act of calling a method inside the other object can cause passive coupling.
     
    Last edited: Jul 23, 2015
    Kiwasi and GarBenjamin like this.
  5. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    I use interfaces for contextual events. If I want clicking on 1 thing to pick it up and clicking on another to use it, I'll put a 'usable' interface on both. It's nice to get the interface component and run the 'use' code instead of determining what the object is. If it's an object where I need to determine what it is, then I may use some abstract class or inheritance, since you can't put variables in an interface.

    A generic item class can be great for things you're going to store in an inventory or equip on the player. You can have an enum for what slots the item can be put in or if it's consumable.

    When recognize that need in a project I go straight for an interface, haha.
     
    Kiwasi and Dustin-Horne like this.
  6. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Let's not forget that you can use both base classes and interfaces. You can have a class that both inherits a base class and implements interfaces, or the base class itself can implement interfaces. There's really no "one is better than the other" because they each have different applications and can be used in combination.
     
  7. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    That's true. And even if a base class could cover a handful of actions (like a container class that can open and close, for cabinets, closets, doors, etc) you could put the useable interface on that and run your code in 'use()' :D

    The only person who will care will be you from the future when/if you want to make updates.
     
    Dustin-Horne likes this.
  8. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    356
    This is generally considered bad practice. There are only a few situations where I can see this being done and I have done it in one, count it...ONE project where it makes sense to do it.

    The standard is to always display/show/declare what interfaces a class is implementing so other developers do not have to inspect each interface trying to locate the interface a property or method is located in.
     
  9. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Its very easy to go from a base class to an interface if needed.

    I think every developer goes through the "everything is an interface!" period. Some developers decide the weather in interface land is nice and set up permanent residence there.

    Just try not to get too caught up on your beautiful architecture, focus on where you need to pump data and how you need to present the data.

    I try to use these rules:
    - Only use inheritance for polymorphism (generally inverting calls) - try to avoid using it for avoiding redundancy (use composition here)
    - Use interfaces when you want to abstract out input or output. Generally best used to make things easy to test in isolation.
    - Try to use single concrete classes as much as possible.

    There is a huge cost in having to navigate code that uses too many interfaces or too much inheritance. Being able to reliably navigate quickly and easily to a methods implementation is incredibly valuable. If you find yourself doing:

    - Go to declaration
    (now in interface)
    - Go to derived class
    (presented with huge list)

    This kind of navigation complexity has a real cost. Having to constantly take multiple steps to find the code being called is annoying and distracting. It should really be used minimally.
     
    Last edited: Jul 23, 2015
  10. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    I think I peaked my head into "everything is an interface" land briefly, and then realized that I only needed interfaces for things that would be shared and vary in function across unrelated things.

    For example...All Items are Picked up the same and only Items can be picked up, therefore I do not need an interface for IPickup.

    However, various items can stack that are unrelated to eachother, so I can create an IStack interface...also IConsume
     
    Ryiah and Master-Frog like this.
  11. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    Interface craziness is one phase I remember, there was another where I kept writing utility classes that did all kinds of stuff with just a single method call, taking objects as parameters and then doing the operations and returning nothing. It seemed like a great way to make the code shorter and easier to read... but all it did was obfuscate. Simple solutions are generally good.

    If you want to get really crazy, you can return the instance of the modified object and chain method calls like jQuery does... even now it seems appealing... could even write a query class to select objects based on id and class...
     
  12. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Honestly, I probably wouldn't use an interface for any of this stuff. But there are so many other factors that go into the discussion that it's sort of hard to talk about in the abstract.
     
  13. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    356
    I just use if (n < 1) instead of the <= comparison.

    I don't see the need to ask "are you less than this value or are you equal to this value right now?".

    I also would not use InvokeRepeating.

    Instead in the method where I decrement the current value I would also check to see if it is less than 1 and perform my destroy object or disable renderer or move it to the junkyard until it is needed again.
     
    Last edited: Jul 23, 2015
  14. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    356

    I agree with this. Every time someone tries to explain interfaces they make them sound more complex than they actually are.



    Technically it is not, in most cases it would make a lot more sense to use a derived class inheriting an abstract class where you can have a bool value of "apple is fruit", BUT if you have many objects that may not be related but may share similar properties, sometimes you can implement an interface across several abstract class objects and/or base class objects.


    However this can quickly lead to interface land where things are not so warm and cozy.
     
    Master-Frog likes this.
  15. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    4pRcYmX.jpg
     
  16. thxfoo

    thxfoo

    Joined:
    Apr 4, 2014
    Posts:
    515
    I just had to remove interfaces from performance critical code and replace it by abstract base classes.

    An interface call is normally slower than a virtual method call (that is to be expected, interfaces allow multiple inheritance, so a indirection more is needed on each call for most cases). In a hot inner loop this can matter a lot...

    That of course means still use interfaces, but recognize when they are a problem.
     
  17. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    You're talking about two different types of "interface", there, both of which serve quite different purposes. The Inspector interface (like any other GUI - "Graphical User Interface") isn't at all the same as a formal interface declared in code. A user interface allows a human to view or modify data inside a machine. A code interface is a contract declaring publicly accessible members.

    And no, you can't always "get the same end result". Imagine that you need to make things all combinations of bouncable and throwable:
    - Bouncable, not throwable
    - Throwable, not bouncable
    - Bouncable and throwable

    There is no single-inheritance hierarchy which can satisfy all of those. To satisfy "bouncable + throwable" through single-inheritance alone you need one to derive from the other, which then violates either "bouncable, not throwable" or "throwable, not bouncable". Interfaces can do it easily.

    Even in cases where you could get the same functionality, I wouldn't consider it "the same end result" since it could potentially take a different amount of work and will definitely impact on the longer term flexibility and maintainability of your code. For example, see my post about the explodables and adding a new exploding object type.
     
    Last edited: Jul 24, 2015
  18. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    This is a pretty neat way to summarise it.
    Unity happens to be one of the most widespread places it's used in game development, but component oriented design is not special to Unity.
     
    Last edited: Jul 24, 2015
    Kiwasi likes this.
  19. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    You're correct, of course. You usually are.

    However, not to detract from what you've said, you can achieve lots of interesting things just like you're saying with components linked via the editor and no interfaces. I just don't see how they work well with that style of development.
     
  20. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    They solve different problems at different levels of abstraction. Components describe or provide aspects of compound objects (GameObjects, in Unity's case), where Interfaces describe aspects of a class of Components (or any other type of class).

    And yes, there are multiple ways to solve most problems, each with their own pros and cons. The more you're aware of the better equipped you are to come to better solutions in a wider variety of cases.
     
    Kiwasi, Dustin-Horne and Master-Frog like this.
  21. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    A simple example: When pooling GameObjects it's common to want to be able to reset them to their initial state when activating them. Different pooled GameObjects may have completely different components in them where this is a concern. A simple way to handle that is to make an IPoolResettable interface and implement that on any relevant components. Then your pool class doesn't have to be aware specifically of what types of components it needs to reset when activating something from the pool - it only has to know to look for IPoolResettables.
     
    Kiwasi likes this.
  22. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    It's definitely a way of doing it. Of course I can think of other ways. My objects are all the same base class, so they would have that ability anyway. The interface aspect seems to me little more than enforced good practices. I hope I'm not ignoring some important aspect of them.
     
  23. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    That important thing is that your objects don't need to have the same base class. That allows you to be far more flexible with how you design things. Plus, the base class doesn't necessarily define all common elements of all sub-classes.

    Also, just having the same base class doesn't help in all cases unless you promote all common functionality to that base class... which defeats the point. Also worth noting, every class is of type System.Object anyway...
     
  24. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Bad practice according to who? There are certainly cases where this is applicable and have nothing to do with hunting down methods. One example is IDisposable. Say you have this interface:

    Code (csharp):
    1.  
    2. public interface IRemoteData
    3. {
    4.     Stream Content { get; set; }
    5. }
    6.  
    Absolutely nothing wrong with the above, however it has an unmanaged resource as a property, therefore it would be a very good idea to protect users from themselves by saying:

    Code (csharp):
    1.  
    2. public interface IRemoteData : IDisposable
    3. {
    4.     Stream Content {get; set;}
    5. }
    6.  
    Now you've moved toward enforcing an actual better practice by telling the user "hey dummy, you have an unmanaged resource here so you better dispose it".

    Now if you've ever written enterprise APIs, it is sometimes very useful to have interfaces implement interfaces for backward compatibility. You can add functionality to a new interface, have it implement the old interface, and have your objects still be interchangeable. Remember this thread was about interfaces in general, not just as related to Unity.



    However, your example is not complex at all and has a huge impact on testability. Here is an example. At my current project we are building out an entire greenfield system for a title insurance company. End to end replacement with an AngularJS frontend split into multiple SPA segments, MVC / WebApi backend for data access. It's also a SOA environment, so things like HR information, document storage, document conversion (we convert to and from PDFs, crop and rotate images in browser, etc), configuration management, etc. are all separate services, as is a workflow, decision engine, and service bus.

    Additionally we've implemented proper enterprise architecture. There are two primary developers (me and one of my Sogeti colleagues) and one lead architect on the project and the client has one of their FTEs assisting. We've implemented the data layer using Entity Framework and a Unit of Work / Repository pattern and all of our business logic exists as separate projects.

    Now, why is all of this important? Simple. We use Unity (the IoC container, not Unity the game engine) for dependency injection. We created a special attribute for our projects and wrote code that automatically pulls all of the contracts from our core contracts project, finds the concrete implementations in the Logic / Data / etc. projects, and wires them up. Now when we create a WebApi controller it looks like this:

    Code (csharp):
    1.  
    2. public SomeApiController(IWorkforceService workforceService, IDocumentManagementService documentService)
    3. {
    4.  
    5. }
    6.  
    And, the concrete implementations are automatically injected when the API Route is invoked. Now let's take this a step further. We have an interface for IUnitOfWork for which we base our UnitOfWork class on. So a logic class might look like this:

    Code (csharp):
    1.  
    2. public OrderProcessingService(IUnitOfWork unitOfWork)
    3. {
    4.     _unitOfWork = unitOfWork;
    5. }
    6.  
    7. public IEnumerable<Order> GetOrders()
    8. {
    9.     return _unitOfWork.RepositoryOf<Order>().FindAll();
    10. }
    11.  
    We're using lazy loading so FindWhere returns an IQueryable and doesn't actually even query the database until we access the data so we can call that method and append to it:

    Code (csharp):
    1.  
    2. var activeOrders = _orderService.GetOrders().FindWhere(o => o.Active == true).ToList()
    3.  
    When .ToList() is called, the actual database query is executed. So why is all of this important? Unit testing of course. You can see it appears that we have a complicated structure, but it's really quite simple and more importantly, fakeable. We are using MSTest to unit test all of our code. Of course, we only care about testing our logic, we don't want to test the actual database and we don't want our unit tests automatically hitting the database, especially since they are also automatically run on every checkin on development and a gated checkin on test.

    So, for our core enterprise stuff we generate stubs and fakes, and for our IUnitOfWork we created an UnitOfWorkFake which is what gets injected. It manages data in memory, uses Lists of T and returns IQueryables so all of our logic works against it exactly as it would the database. It also allows us to do watches so we can conditionally check to see if SaveChanges() was called on the unit of work and apply it to our tests.

    Basically what I'm saying is, for small projects, yeah, you are probably ok to avoid the complexity. For something like Unity development you probably are ok to, though if you build logic in a separate assembly it would be nice to have that testable, but if you want to get into serious development, especially on a large scale, you have to understand when the importance of the implementation outweighs an extra couple of mouse clicks.

    Additionally, CodeLens in visual studio allows you to "peek definition" in addition to goto definition, so you can follow definition trails without opening a new document until you get where you need to go. That used to be a ultimate only feature, however the restructuring of MSDN brought CodeLens to visual studio standard now (which means it should be in community edition, though I haven't checked as I have an enterprise MSDN).
     
  25. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    Might not be the best example, if for no other reason than bouncable and throwable imply behavior (F***, I keep spelling behavior with a u). Now that I think about it, behavior might be a better delineation for the difference between regular inheritance and interfaces, rather than the single/multiple inheritance issue.

    Inheriting from a class can/should/will imply that the new class and base class behave similarly (usually either modified internally, but externally identical, or extended upon, with base functionality barely touched). Interfaces should have few if any assumptions about what their internal processes are (the fact that all interfaces require a new implementation for any of it's functions is a part of this).

    Single/multiple inheritance is a totally separate issue. Multiple interfaces just means there are multiple ways to interact with an object, but having multiple pre-established behaviors is not possible because of single inheritance (before you get to components at least, but that'll be another entire forum thread).
     
  26. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    True. I simply mentioned it as another relationship that is fundamental to how Unity works. Most intro coding videos only show inheritance and interfaces.
     
  27. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    I don't mind implying behaviour with an interface. I know that ideally you'd do that with inheritance, but sometimes that's not possible and/or necessary. Also, just because two things can both be thrown doesn't necessarily mean that their throwing implementations are the same. Again, that would be ideal most of the time, but there are cases where it doesn't work.
     
  28. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    A
    A knife is throwable and a barrel is throwable but you're probably not going to put the barrel into your pocket (inventory).
     
    angrypenguin likes this.
  29. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
    Dustin, I respect your work - considering your asset is one of very few code based assets that I haven't ended up ditching. I've worked on some very serious programs that dealt with very serious amounts of money as well. And sure, certain environments require certain compromises.

    Right now, my compilation time is around 12-15 seconds from the Unity side (visual studio compiles in around 4 seconds). This might not sound like that big a deal, after all it's just 12-15 seconds. The problem is that 12-15 seconds is just long enough for me to do something like check these forums, and in doing so, often I end up reading posts or posting (almost every post I write here is because I'm waiting for compilation). So that 12 seconds actually ends up frequently costing me 15 or 20 minutes. In the last few months, I've written maybe 100 posts, at an average of 15 minutes...that's something like 25 hours. The reality is, the real number of hours is probably at least double that. So it's not unreasonable to say that the 12 seconds of compilation has cost me 50 hours of lost productivity in the last few months. That's ... crazy (I gotta stop reading these forums).

    There are a lot of hidden costs to stuff like mouse clicks, or wait time. Mental fatigue is a big factor as well. Every small layer of indirection adds up. I used to just think of these steps and costs as "this is how you do it". But if you can find ways of doing most of that work without the indirection, and you control click that method call and it just goes to the right method instead of going to the interface definition, where you need to right click -> read list of 15 popup context menu options -> to implementation -> read list of 20 classes, pick the right one then finally look at the code you need to look at, only to have to do it again three lines down...

    Indirection, abstraction, these things are useful tools. But these little costs add up, and they're much more significant than most of us think.
     
    Petter_H likes this.
  30. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    Thanks, and I wasn't meaning to direct anything personally, it was more of a generalization and like I said, I was trying to extract the interface discussion from being Unity specific. I agree that there are times when that productivity adds up, my point is just that one most projects, at least ones where you're doing heavy amounts of unit testing, it's almost a wash because it saves you time elsewhere. For performance critical things we have to be much more conscious of what implementations we choose.

    As an example, in my current engagement build time isn't really a consideration. It doesn't take long to build on our somewhat beefy dev machines. When we check in though, the CI builds take forevery (11 to 15 minutes on some solutions) because they run static code analysis and auto-run the unit tests. In unity however, build times can get out of control and kill productivity in a hurry.

    By the way, I might be able to help cut your build time depending on what platforms you're targeting. It is possible to pre-compile JSON .NET for Unity without making any changes for some platforms. For others you have to use special folders for the DLLs but it's a pretty easy setup.
     
  31. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    @frosted I'll send you a PM so as not to hijack this thread.
     
  32. frosted

    frosted

    Joined:
    Jan 17, 2014
    Posts:
    4,044
  33. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    356
    Any developer who works a lot writing interfaces, they tend to avoid implementing interfaces into interfaces just like how it is bad practice to chain classes together through inheritance. It is cool, but leads to problems.

    IDisposable is a good example of a use case where needed; but you normally do this at the class level (or abstract class level) and usually will not implement it into another interface. You CAN but I would not make it a general practice in my day to day development unless it was unavoidable or helped with optimization.

    Consider the following:

    Code (csharp):
    1.  
    2. public interface IBreakable { }
    3.  
    Code (csharp):
    1.  
    2. public interface IRepairable { }
    3.  
    Code (csharp):
    1.  
    2. public interface IDamageable { }
    3.  
    Code (csharp):
    1.  
    2. public interface IHealable {}
    3.  
    Code (csharp):
    1.  
    2. public interface IContainer { }
    3.  
    A standard approach would be to create a base class (or an abstract class) and have that class implement each interface it requires in this case a Thug class can be damaged, it can be healed and it can be a container for loot.

    Now a class object of Barrel is a container, it can be damaged, it will break when damaged, it can be repaired when broken, but it CANNOT be healed.

    So you get the following...

    Code (csharp):
    1.  
    2. public class Thug : IDamageable, IHealable, IContainer
    3. {
    4. }
    5.  
    Code (csharp):
    1.  
    2. public class Barrel: IContainer, IDamageable, IBreakable, IRepairable
    3. {
    4. }
    5.  
    However if someone thought it would be a good idea to chain implement interfaces that are related you end up with...

    Code (csharp):
    1.  
    2. public interface IDamageable : IHealable, IBreakable, IRepairable { }
    3.  
    Code (csharp):
    1.  
    2. public interface IContainer { }
    3.  
    Now when we implement IDamageable in the Thug base class were also getting IHealable, IBreakable and IRepairable as well, where it clearly does not make sense for the thug class to have IBreakable or IRepairable.

    This can lead to bad practices to implement interfaces into interfaces simply because they are related at the abstract level. A Thug can be damaged and can be a container, just like a barrel can also be damaged and is a container, but the barrel can also be broken and repaired.

    Instead my base classes would look like...

    Code (csharp):
    1.  
    2. public class Thug : IDamageable, IHealable, IContainer
    3. {
    4. }
    5.  
    Code (csharp):
    1.  
    2. public class Barrel : IContainer, IDamageable, IBreakable, IRepairable
    3. {
    4. }
    5.  
    Here you can clearly see what class implements what interface. This is cleaner and promotes good coding practice especially with large teams, and if a class needs IDisposable you simply add it at the class level.
     
  34. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    I think all of the developers you know must sit next to you. I can't name one (and by the way I just asked my lead architect just out of curiosity as well). It is situational. The example you provided was very valid, however the IDisposable argument falls apart.

    If you're implementing an SDK for example, you may expose an interface that allows a user to build and register components. You need to know that those components implement IDisposable which requires it to be defined at the interface level. If it's just defined at the class level you lose any and all ability to make that guarantee goes out the window. You can check for it via reflection, or in some cases you may have the ability to filter it via generic costraints (i.e. where T : IDisposable), however that's not always possible.
     
  35. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    I've only gone as far as 1 super class so far :D I would have stuck with interfaces, but I needed some variables too.
     
  36. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    356
    You can have variables in interfaces too. Its not just methods.
     
  37. Dustin-Horne

    Dustin-Horne

    Joined:
    Apr 4, 2013
    Posts:
    4,568
    You can have properties, not variables and not fields.
     
  38. RockoDyne

    RockoDyne

    Joined:
    Apr 10, 2014
    Posts:
    2,234
    You can set properties in an interface, but the last time I put a variable in an interface it was pretty explicit in saying you can't do that.
     
    Dustin-Horne likes this.
  39. Master-Frog

    Master-Frog

    Joined:
    Jun 22, 2015
    Posts:
    2,302
    That a discussion can go on this long, and there's still no unified viewpoint, is why I tend to avoid them.
     
  40. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    Why have an interface, but then test it against all kinds of types to see what it is?
    Usually, with an interface you don't care what type it is that you're actually using, you only care that it implements the interface.
     
    angrypenguin and Dustin-Horne like this.
  41. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    I don't test the type... I check if the object implements the interface using GetComponent<Interfacename>()
     
  42. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    I was referring to these lines:
    Code (CSharp):
    1. actionEat.SetActive((itemRef is IUse) ? true : false);
    2. actionEquip.SetActive((itemRef is IEquip) ? true : false);
    3. actionReload.SetActive((itemRef is IReload) ? true : false);
    4. actionDrop.SetActive((itemRef is IDrop) ? true : false);
    or are all of these derived from a different base class / interface ?
     
  43. derf

    derf

    Joined:
    Aug 14, 2011
    Posts:
    356
    Not trying to single you out Dustin. ;)

    Yes I meant property, but a property is usually backed by a variable anyway. Even if you have an auto property implemented. Not all properties are backed by variables though, I am aware of this, but the vast majority are.

    I always try to use properties when accessing data
     
  44. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    Different interfaces. This is just to toggle the appropriate UI buttons.

    At some point Food and Ammo may be base classes.

    Ammo vs Food.
    Item : MonoBehaviour, IUse
    Ammo : Item, IStack, IDeplete
    Food : Item, IConsume

    Capture.JPG

    Capture1.JPG
     
    Last edited: Jul 25, 2015
  45. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    I noticed the property thing but I got warnings about fields when I tried to do anything. Very confusing :) I'll stick to subclasses so I can pretend I have a use for them.
     
  46. Slyder

    Slyder

    Joined:
    Oct 17, 2013
    Posts:
    270
    Workaround was earlier in the thread :p
    Code (csharp):
    1.  
    2. public class Ammo : Item, IStack, IDeplete, IAttach {
    3.     public Bullet bullet;
    4.  
    5.     [SerializeField]
    6.     public int _stackSize;
    7.     public int stackSize {
    8.         get { return _stackSize; }
    9.         set { _stackSize = value; }
    10.     }
    11.     public int stackBulk {
    12.         get { return _stackSize*itemBulk; }
    13.     }
    14. //blah blah blah
    15. }
    16. ..................................
    17.  
    18. public interface IStack {
    19.     int stackSize {get;set;}
    20.     int stackBulk {get;}
    21.  
    22.     Item SplitStack(int newStackSize);
    23.     void StackCleanUp();
    24. }
    25.  
    26.  
    27.  
     
    Tomnnn likes this.
  47. Ryiah

    Ryiah

    Joined:
    Oct 11, 2012
    Posts:
    21,175
    While lacking a unified viewpoint could be taken as having no clear advantage, it could also be seen as having no wrong way to use them. If anything that makes me even more inclined to try using them in future projects as I won't feel like I'm hurting myself by doing something wrong.

    This thread has been a fantastic read so far and I may find myself referencing it a bit in the future.
     
  48. Tomnnn

    Tomnnn

    Joined:
    May 23, 2013
    Posts:
    4,148
    So you can use 'variables' as long as you define a get and set? My use is...

    BaseAction - contains a basic loop and a state variable
    BaseController - contains a basic update loop and a few variables

    Would interfaces with properties be better than having a base class? I literally have never had a class with more than 1 parent yet :p
     
  49. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    To be fair, if someone at work asked for feedback on an IDamageable interface as per your example I'd suggest breaking the others out of it because that combination is use-case specific. I would not assume that just because something can be damaged it can necessarily also be healed, repaired and/or broken, so those interfaces should be included explicitly where they're true. This would improve both readability and flexibility.

    On the other hand, if something can be repaired then I it might be a safe assumption (depending on the system at large) that it can also be damaged. Same deal with breakable, as I'd expect breakage to be an effect of damage. So I'd encourage considering flipping some of those chains around, if it makes sense in the system at large, because that might encourage more consistent use of repairable and breakable things (eg: if the system is designed such that breakage should be a result of damage then it makes sense to enforce use of the damage interface rather than assume it).

    I agree with what I think you're driving at, though, which is that enforcing anything is always a double-edged sword.
     
    Slyder and Dustin-Horne like this.
  50. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    What? Form your own viewpoint. Nobody else's matter except for what you might be able to learn from them.

    These things are all just tools. As long as you're getting your jobs done inside your budgets and timelines and you're constantly improving then it doesn't really matter how you're using what combination of tools to get there. The point of the discussion is to learn from one anothers' experience in pursuit of that constant improvement. It's not about finally unearthing The One Right Way, because there isn't one.