Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Virtual Interfaces

Virtual Interfaces

Presented at Coolblue's Behine the Scenes Delphi meet-up

Michał Kulczycki

November 29, 2018
Tweet

Other Decks in Programming

Transcript

  1. Agenda Things I hope to cover: • Vanessa: Coolblue’s ERP

    • Interfaces of DB components • Virtual interface as a generic implementation • Examples and real code • Tips and tricks (and warnings)
  2. No easy wins • Millions of lines of code •

    Developed over last 15 years • Roughly 2000 functions • 90% with their own data-modules • Each containing 2 - 70 DB components • No unit tests
  3. Data modules: state 1. Client datasets and lookups (for DB-aware

    controls), 2. Various utility procedures and view properties, 3. Lots of inherited functionality for the view VIEW DB DATAMODULE
  4. Data modules: DB access 1. Customised ADO queries, commands, stored

    procedures etc., 2. Using common objects shared by hundreds of DMs, 3. Including connection object from main form’s DM (globals, yay) VIEW DB DATAMODULE
  5. Data modules: business logic 1. Usually tied to state (e.g.

    selected rows) 2. Resulting in direct DB access 3. Mixed with APIs, some procedural code and other DMs VIEW DB DATAMODULE
  6. Solution: a humble wishlist • Eventually replaceable by APIs and

    domain objects • Zero DB types exposed (incl. TDataset & TField) • SQL moved from DFMs to … somewhere (and kept static) • Supporting all types of DB objects and DDL/DML operations • Independent of client/driver (e.g. ODAC instead of ADO) • Making logic fully unit-testable (thanks to total abstraction) We wanted something:
  7. One small problem Implementation of some specific interface is easy.

    Multiple implementations also pose no problem. But all boils down to something like: An interface requires some implementation. And we did not want to write thousands of those...
  8. Virtual interfaces are a thing! Virtual interface is: • an

    actual Delphi library class called TVirtualInterface • descending from TInterfacedObject (ref-counted) • living somewhat forgotten life in System.Rtti
  9. Virtual interface’s expectations TVirtualInterface instance is created with two unusual

    parameters: 1. RTTI’s type information (PTypeInfo) about some interface, 2. OnInvoke event handler it will call when invoked: .. and that is pretty much it!
  10. Virtual interface’s birth It’s whole purpose in life is to

    do two things only: 1. Pretend that it actually is an implementation of that interface, 2. While transparently forwarding all its method calls to OnInvoke event.
  11. How is that useful? • Class does not have to

    be declared as an implementation of an interface • Handler (implementation) has to know how to adjust its behaviour based on: • RTTI information (type description: names, types, attributes etc.) • arguments (just like any other method accepting those). So, if we can fit all control information into type declaration and normal arguments, we are going to get ourselves a generic implementation of some kind of interfaces. Like any and all SQL statements.
  12. What about APIs? • Initially generated from our OpenAPI Delphi

    code generator • Generated code is quite simplistic and rigid • But: implementation is repeatable • Most complex DTOs can be easily marshalled into JSON • Special cases can be handled with attributes. SERVICE 2 API DB
  13. RTTI must be requested RTTI information is crucial but Delphi

    won’t generate it unless your interface: 1. inherits from IInvokable: 2. or is preceded with {$M+}: Fun fact: First one works 99.9% of the time. Until it doesn't.
  14. It walks like a duck, but isn’t one • Virtual

    interface instance acts like an interface. Runtime. • You can cast an instance, but you need one first. • Delphi (and Spring container) will rebel against it:
  15. Careful with generics • Generic bloat has been reduced but

    still is significant. • Sizeable implementations will bloat your binaries (biggest Vanessa DCU: registration of 1 API + 8 statements), • Generics are useless during type inspection: you’ll do TypeInfo(T) and proceed with PTypeInfo anyway • Use generic stub to: 1) Do that cast and, 2) Pass resulting PTypeInfo to an actual implementation.
  16. Separate type inspection from execution RTTI data inspection during an

    Invoke is wrong on many levels: • Will be executed multiple time (RTTI is fast enough, but still…), • Often will be too late: context can depend on previous call, • It is a bit too late to learn that something is missing or wrong, Instead: • Inspect type once, preferably before instantiation, • Raise inspection errors, log warnings and information, • Cache results in some structure and just serve it, • Every new instance will be fully operational instantly, • Bonus: they won’t need generics and maybe even PTypeInfo.
  17. Self comes first • Args[0] is not first argument of

    the Method invoke (It will be in Args[1] if there were any (of course) • Args[0] is always equal to Self ( in TValue) • Failure to rember that will result in craziest of run-time errors