What is the “tear-off” pattern?
The idea of a "tear-off" is that we have some functionality provided by an object, but the thing actually providing the functionality does not have identity with the service provider you're talking to.
Cast your mind back to the 1990s and imagine that you're a COM programmer. COM is a binary standard for doing object-oriented programming from languages that do not necessarily support objects directly, like C. The foundational method in COM programming is
IUnknown::QueryInterface, which takes a number that uniquely identifies an interface and either fails, or succeeds by providing a pointer to an object that implements that interface. All COM interfaces inherit from
IUnknown, and therefore all COM objects implement
QueryInterface on all their interfaces.
So suppose you are implementing your COM server -- remember, a server is any object that provides a service; don't think of a server as a machine -- as a C++ class and you want it to implement
IBar, both of course deriving from
IUnknown. Typically we do this by creating abstract classes for
IBar, assigning each a globally unique number, and then implementing
QueryInterface on a C++ class such that it casts the
this pointer to the appropriate abstract class and returns the cast pointer. The standard way to implement multiple interfaces is multiple inheritance of abstract base classes. (Note that even though we logically are preserving referential identity, in practice the pointers are allowed to be unequal and usually are.)
But all that is required of
QueryInterface is that on success it returns a valid pointer to an object that has the right vtable. There is not a requirement that
QueryInterface be implemented via casting to interfaces that are multiply inherited. It would be entirely legal for you to implement your contract by having
QueryInterface return a different object entirely when QI'd for
IFoo and a different object entirely when QI'd for
IBar. (There are then some complications regarding what happens when you QI the
IFoo object for
IBar, but perhaps you can see how those are handled.)
There are performance and program architecture reasons to prefer this implementation choice, which is called "tear-off interfaces". The key thing is that when asked for a service, the object hands you back a different object that implements that service, rather than implementing the service itself.
From a "design patterns" perspective, I think of the "tear-off" pattern as being the first step towards more complex solutions to this problem such as "chain of responsibility". In "chain of responsibility" you ask an object for a service and it either provides it, or it says "I can't provide the service you want, but I know someone else who might..." and forwards the request along a chain of providers. (In COM, "chain of responsibility" is represented by IServiceProvider::QueryService.)
It appears that Dart is using "tear-off" to mean something similar, but only for a single function, not for an entire interface. That is, a "tear off" is an object that is distinct from the original object, but represents a service provided by that object, namely, the ability to call a method. I would think of this in C# as a delegate.