Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add(I*VirtualObject): Add interfaces for all Vob types. #12

Merged
merged 2 commits into from
Sep 20, 2024

Conversation

JaXt0r
Copy link
Collaborator

@JaXt0r JaXt0r commented Sep 17, 2024

Hint: This PR leverages Option 2) Interfaces.

Description

On our C# project, we have a Lab scene where we test certain conditions. E.g. testing door animations of a locked instance of Door. The base class VirtualObject is already baked by an interface (IVirtualObject), which makes it easy to create a test class and overwrite certain behaviour. The super classes (like Door) are without interfaces so far. To ensure a proper way of subclassing e.g. Door with proper overwriting of functionality, we either need:

  • Option 1) Every property of a subclass of VirtualObject (like Door) need to make their properties virtual so that we can overwrite them
  • Option 2) Otherwise every subclass needs to be baked by an interface

Examples

Current behaviour

// ZenKit
class Door : VirtualObject
{
    public bool IsLocked => Native.ZkDoor_getIsLocked(Handle);
}

// Our C# project
class LabDoor : Door
{
    // Hint: New is indicator for compiler warning only. It won't affect handling below.
    public new bool IsLocked => true;
}

class Lab
{
    public void Execute()
    {
        // In our example, we instantiate a LabDoor, but in our test scenarios, we only know, that we leverage a door. LabDoor is kind of a mock. But testing normal code, we never! know if it's a Lab object.
        Door testDoor = (Door)new LabDoor();

        // If we do _not_ mark Field as virtual, we will always execute superclass Field. (Door.IsLocked)
        testDoor.IsLocked;

        // In current state, we would need to cast it back to Subclass, which we don't want when we test normal (non-Lab) code.
        ((LabDoor)testDoor).IsLocked;
    }
}

Option 1) Virtual method changes

// ZenKit
class Door : VirtualObject
{
    public virtual bool IsLocked => ...
}

// Our C# project
class LabDoor : Door
{
    public override bool IsLocked => true;
}

Option 2) Interface usage

// ZenKit

class IDoor : IVirtualObject
{
    bool IsLocked { get; }
}

class Door : VirtualObject, IDoor
{
    public bool IsLocked => ...
}

// Our C# project
class LabDoor : IDoor
{
    public bool IsLocked => true;
}

Conclusion

  • I leveraged Interface behaviour for all subclasses of VirtualObjects because - well - Composition over inheritance. (Jokes aside, the Interface way felt more transparent than _virtual_izing every method)
  • I also inherited every Interface with it's parent interface (e.g. IDoor : IInteractiveObject) as it makes it easier for us to just have an IDoor object where we can fetch all the I* properties instead of casting it again)
  • I executed all ZenKit.Tests. If you want me to create tests for the Interface logic, just tell me what to execute.

@lmichaelis
Copy link
Member

lmichaelis commented Sep 17, 2024

Generally, I like this change, but since all VirtualObject subclasses are in and of themselves VObs, I think that IVirtualObject should be baked into all of these new interfaces, so that multiple inheritance is not required any more. E.g. interface IDoor : IVirtualObject and then class Door : IDoor (that's a bad example because the inheritance tree is deeper for Door but you get the gist)

@JaXt0r
Copy link
Collaborator Author

JaXt0r commented Sep 17, 2024

Thanks for your feedback. We're aligned, that Sub-Interfaces should also inherit parent interfaces.

e.g. IDoor : IVirtualObject <-- As Door extends VirtualObject, we also reflect this inside the Interfaces.

I also updated Fields and their return values. So that e.g. Item SomeField is now altered to IItem SomeField.

@lmichaelis
Copy link
Member

Cool, looks very nice now :)

@lmichaelis lmichaelis merged commit fa8d331 into main Sep 20, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants