Here's how I would approach this. First define each of those entities as separate classes:
```python
class Foo: ...
class Bar: ...
class Baz: ...
```
Next define a generic abstract handler:
```python
T = TypeVar('T', Foo, Bar, Baz)
class Handler(ABC, Generic[T]):
@abc.abstractmethod
def handle(self, obj: T):
pass
```
Now we want to store handlers per object type. We will utilize a dict of lists for that. But first lets define a scanner:
```python
class Scanner:
def __init__(self, handlers):
# handlers is a dict of lists of Handler instances
self.handlers = handlers
def _apply_handlers(self, obj):
# additional checks and logs here
obj_type = type(obj)
for handler in self.handlers[obj_type]:
handler.handle(obj)
def run(self, input):
for chunk in input:
... complicated logic ...
obj = None
if ... something ...:
obj = Foo(...)
if ... something else ...:
obj = Bar(...)
if ... something else ...:
obj = Baz(...)
if obj is not None:
self._apply_handlers(obj)
else:
# log it?
```
and finally the corresponding builder:
```python
class ScannerBuilder:
def __init__(self):
self.handlers = {}
def register(self, obj_type: Type, handler):
# This could be implemented with just handler parameter,
# and with inspection magic, but that is just simpler.
# Although it might be needed anyway for proper validation.
if obj_type not in self.handlers:
self.handlers[obj_type] = []
self.handlers[obj_type].push(handler)
def build(self) -> Scanner:
return Scanner(self.handlers)
```
And finally the usage:
```python
builder = ScannerBuilder()
builder.register(Foo, FooScanner)
builder.register(Bar, BarScanner)
builder.register(Baz, BazScanner)
builder.register(Baz, OtherBazScanner)
scanner = builder.build()
scanner.run(input)
```
Looks like cleanly separated solution to me.