codebude / QRCoder

A pure C# Open Source QR Code implementation
MIT License
4.59k stars 1.09k forks source link

[WIP/QRCoder2] Fluent API experimentation PR #558

Open Shane32 opened 4 months ago

Shane32 commented 4 months ago

Summary

Experimentation with fluent API syntax

Samples

var code = QRCodeBuilder.CreateEmail("test@microsoft.com")
    .WithSubject("Testing")
    .WithBody("Hello World!")
    .WithErrorCorrection(QRCodeGenerator.ECCLevel.H)
    .RenderWith<AsciiRenderer>()
    .WithQuietZone(false)
    .ToString();

var image = QRCodeBuilder.CreatePhoneNumber("1234567890")
    .WithErrorCorrection(QRCodeGenerator.ECCLevel.H)
    .RenderWith<SystemDrawingRenderer>(20)
    .WithQuietZone(false)
    .ToBitmap();

var base64 = QRCodeBuilder.CreateMMS("1234567890", "Hello")
    .RenderWith<PngRenderer>()
    .ToBase64String();

Intellisense samples

image

image

Preliminary Findings

  1. Could start with new QRCodeBuilder.Email("test@microsoft.com") rather than QRCodeBuilder.CreateEmail("test@microsoft.com") - which is pretty similar to the existing new PayloadGenerator.Wifi() syntax actually, with the difference being the fluent syntax for configuration. One benefit of using new is that additional payloads can be added by simply adding a new class into the correct namespace. Whereas methods cannot be added to QRCodeBuilder outside of QRCoder.
  2. Using RenderWith<T> breaks the intellisense pattern because T does not give a list of the specific renderers available. Probably better to create an extension method for each renderer, like RenderAsAscii() and RenderAsPng(20) instead
  3. RenderWith<T> does not allow for render-specific constructor parameters. I've compensated with support for only two patterns: (1) no arguments (2) pixels per module argument. Having RenderAs...() dedicated methods would allow each renderer to require specific arguments.
  4. The builder syntax could likely be added to v1.x as a new layer, as shown in this PR, with any implementation changes we wish to make within the builder methods. Then v2 just removes (or makes internal) all the old methods.
  5. The new builders, when layered on the old code, are quite easy to write. Once the old code is removed, additional optimizations can be added to enhance trimming support. This trimming capability may consolidate the QRCode and ArtQRCode classes.
  6. All of the supporting code can be nested as deep within namespaces as desired, since intellisense always provides the correct context-sensitive methods. Except for RenderWith<T>, as noted in item 2 above. Another reason to use RenderAsAscii() or similar.
  7. The extension methods on the renderers (ToArray, ToStream, ToFile, etc) are really cool -- each renderer need only implement ToStream and all the other methods are available via extension methods. Similar functionality for text renderers; even with only implementing ToString they can also provide ToFile, ToStream, ToBase64, and so on via extension methods.
csturm83 commented 4 months ago

The builder syntax could likely be added to v1.x as a new layer, as shown in this PR

QRCoder2.FluentExtensions maybe? 😉

codebude commented 4 months ago

Hi Shane, thanks for the PR. That looks pretty interesting. However, I don't want to merge this into v1.x, but would like to include it in v2 first, as I don't want to make/document/communicate any more major API changes to v1. I hope that's okay with you.

Shane32 commented 4 months ago

Certainly. I wrote this more of a discussion/preview/review of a potential approach.