Forbid extending my class

Primary tabs

Answers

I think you're looking for the Final keyword

Final keyword can be removed and the class extended.

To clarify, I want to deploy a class that the user would be unable to extend while having access to the system.

So you want to prevent users from changing the source code of the class. 

Deploy it without a source code?

Deploying does not prevent user from removing Final keyword.

Deploying only prevents user from easily modifying source code.

How could one remove Final keyword without changing the source code?

set c=##class(%Dictionary.ClassDefinition).%OpenId("Deployed.Class")
set c.Final=0
write c.%Save()

And after that compiling subclasses would be possible.

Is there a way to prevent successful %Save() for the ClassDefinition?

Is ClassDefinition available even if you deploy without the source code e.g. OBJ version?

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. 

ReadOnly database could be made Read-Write. I assume developer has complete access to Cache and underlying OS.

meta data of class definition is included in the read only database

It is.

I think all this trench-digging is becoming a somewhat pessimistic perspective, but indeed if you assume someone has admin access, they're admins and can take administrator action...

Maybe this is what lawyers invented licenses for :-) (agreeing that that's then becoming a somewhat simplistic perspective)

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).

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.

I'm glad you found the solution, though still not sure about its usefulness :)

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. 

You are right. But I believe there are cases.

I don't know why Eduard needs it but e.g. if you consider implementing licensing on your ObjectScript solution with controlling logic in ObjectScript I believe you don't want the method of license checking to be overloaded. Final method in deployed mode could be a solution in this case.