[swift-evolution] Will Swift ever support optional methods without @objc?
Rick Mann
rmann at latencyzero.com
Tue Nov 15 17:41:44 CST 2016
> On Nov 15, 2016, at 09:24 , Dave Abrahams via swift-evolution <swift-evolution at swift.org> wrote:
>
> Speaking for myself: I think “probe-a-type” programming is in general a
> bad idea, and I'm opposed to adding features that encourage it. It's
> almost always better to design entry points into supertypes (protocols,
> or base classes if you must ;->) with default implementations that can
> be overridden to do the work you need based on the characteristics of a
> subtype, rather than trying to make decisions in the caller based on the
> shape of the type. When you *do* need probing, it's a good idea to make
> the set of possible subtypes a closed set, so the compiler can ensure
> you handle all cases—i.e., use an enum.
(I'm re-watching your WWDC 2105 talk about protocol-oriented programming, so I may end up with answers to the issues below.)
The thing that prompted my original post on this subject was an exploration of how to best implement a CAD program. In this case, something like Illustrator, where you can create, manipulate, and draw various shape types. I wanted to separate, as much as possible, the mathematical representation of the shape from the drawing and manipulating, and ideally have only one set of objects permanently instantiated (the mathematical representations of the shapes, i.e. the model).
So, I have a CanvasView that has a list of these shapes (a simple array). I have a set of tools (generally, a single instance of each, although when each gets instantiated can be changed in the design). The CanvasView triggers the drawing and the mouse handling for the set of shapes (CanvasObjects).
I've got something like this:
protocol Renderable { func renderer() -> Renderer? }
extension Renderable { func renderer() -> Renderer? { return nil } }
protocol Selectable { var selectionState: enum SelectionState }
protocol HitTestable { func hitTest(_ inPt: CGPoitn) }
protocol CanvasObject : HitTestable, Selectable {}
class Path : CanvasObject { }
protocol Renderer { func draw(in inCTX: CGContext) }
class PathRenderer : Renderer { }
You get the idea. Stop me if I'm way off base here.
I initially wanted Renderable.renderer() to be optional. I think I'm convinced returning an optional Renderer is equivalent.
Drawing
-------
CanvasView visits each CanvasObject in turn, instantiates an appropriate Renderer for the object (by calling Renderable.renderer()), calls Renderer.draw().
Mouse Handling
--------------
The currently-selected Tool is handed the mouse hit and the view. It iterates the objects, calling hitTest(). It then instantiates a handler based on the hit object type and current tool (might just be the current tool).
Problems
--------
The CanvasView has a list of CanvasObjects, forcing all objects to conform to all the protocols (e.g. Renderable, Selectable, HitTestable.) That is the CanvasView has a view onto the set of objects as complete objects, rather than a view of all Renderable objects, all HitTestable objects, etc. In this app, it may not be meaningful to talk about objects that are only renderable, but then again, it might (a grid could be renderable, but not selectable or hit testable; in practice it'll be implemented as a completely different entity, not part of the set of objects).
There is a problem of state management. Shapes are drawn differently depending on their state (e.g. normal, hovered, selected, sub-selected, being-snapped-to, etc.). I see no good way to store that state, except in the core object (e.g. Path). Alternatively, a parallel data structure that holds that state, but now I have to ensure instances of associated objects can be mapped back and forth. In fact, as I think about this more, I think it's a better solution, because it's conceivable there would be multiple views of the same model data, and the various states might be independent per view.
And performance will probably be okay, but needs to be considered.
I'm probably just making a mountain out of a molehill, but I want to make sure I'm understanding Swift thoroughly.
--
Rick Mann
rmann at latencyzero.com
More information about the swift-evolution
mailing list