Discussion (21)3
Log in or sign up to continue

I believe it's enough to have [Final] keyword set in deployed mode to give a developer a hint that this class should not be extended.

Well, it's mainly for developers who can't take a hint.

If you want to enforce this behaviour, I would add a check into each method as a first line , something like

if $this.%ClassName(1)'="My.Class" quit $$$ERROR(50000,"don't extend this class")

Good idea.

You can also try to add a method-generator, I believe when you have a deployed class with method generator it will not be able to compile a subclass without method generator's source (though I'm not sure).

At first it didn't work - method generator by default works while deployed. Then I added a class check to generator.  Compilation was now failing, but other methods were actually compiled so I was able to call them. Finally I worked out this solution:

Class Package.Final [ Final ]
{

ClassMethod ANoExtend() [ CodeMode = objectgenerator, Final, ForceGenerate, Private ]
{
    quit:%class.Name="Package.Final" $$$OK
    quit $$$ERROR($$$GeneralError, "No extending")
}

ClassMethod ProtectedMethod() As %Status [ Private, ForceGenerate, GenerateAfter = ANoExtend ]
{
        // code
    quit $$$OK
}
}

This way each protected method should be recompiled but only after method-generator which always fails in subclasses. This way no code gets generated.

This is probably an aggressive approach but you could try and do the following:

  • Create a code database with your class marked with final in it
  • Deploy said code with class marked as final and mount code as read only
  • Map the read only code database to the namespace that needs to access it

This should prevent a developer from being able to inherit from the class while still being able to access it. This is also an assumption that the meta data of class definition is included in the read only database. If it isn't, that's a very interesting flaw of what a read only code database means. 

Agree on your "usefulness" statement. Making a class non-extendable breaks all recommended development practices. For example TDD. I won't be able to mock this class for testing purposes and will be forced to test with your class only (Granted Cache is not strong typed so in theory I have ways to bypass this limitation but still). 

Another recommended principal is to use composition instead of inheritance so in theory I could do exactly the same I wanted to do with your class (after you "lock it down") by making it part of another class without extending it. If a developer wants to get around your class ( don't know the reasons why) he/she can by other means unless you plague your code with a bunch of type checks (making Cache strong typed). 

Even if a developer get to extending your class, the fact you can't overload methods in Cache gives you a level of restriction when combined with deployed code and final as there's "no way" a developer can change your implementation so your code will still call your methods' implementation on a subclass.

All and all it seems you're going to extremes for no real benefit but since we don't know the whole picture maybe this effort is worth it. 

I believe it's enough to have [Final] keyword set in deployed mode to give a developer a hint that this class should not be extended.

If you want to enforce this behaviour, I would add a check into each method as a first line , something like

if $this.%ClassName(1)'="My.Class" quit $$$ERROR(50000,"don't extend this class")

Since all code will be deployed, developers will not be able to remove this check easily.

You can also try to add a method-generator, I believe when you have a deployed class with method generator it will not be able to compile a subclass without method generator's source (though I'm not sure).