Correct pattern for "instantiating the instance"

87 Views Asked by At

Note: In this question I am using Python syntax and Python terminology, but I am asking for the conceptual approach, independent of actual programming language, to make sure that answers would be useful for broader audience.

Level 0

Let's start with few functions:

def dog_tell(name, target, what):
    print(f"{name} the dog tells {what} to {target}")

def dog_bark(name, target):
    print(f"{name} the dog says 'Bark at you, {target}'")

def dog_wag(name, target):
    print(f"{name} the dog wags tail at {target}")

def dog_woof(name):
    print(f"{name} the dog woofs")

To use these functions, first argument is expected to be the dog name, so it would do something useful like this:

>>> dog_bark("Jake", "Finn")
Jake the dog says 'Bark at you, Finn'

Level 1

Now, if we are using these functions with the same dog a lot, it makes sense to group these functions together, and only provide the dog name once. There are at least two approaches to do that.

OOP

OOP approach is to declare the Dog class and provide it with name:

class Dog(object):
    def __init__(self, name):
        self.name = name

    def tell(self, target, what):
        print(f"{self.name} the dog tells {what} to {target}")

    def bark(self, target):
        print(f"{self.name} the dog says 'Bark at you, {target}'")

    def wag(self, target):
        print(f"{self.name} the dog wags tail at {target}")

    def woof(self):
        print(f"{self.name} the dog woofs")

>>> jake = Dog("Jake")
>>> jake.bark("Finn")
Jake the dog says 'Bark at you, Finn'

Functional

An alternative approach would be currying:

name = "Jake"
jake_tell = lambda *args: dog_tell(name, *args)
jake_bark = lambda *args: dog_bark(name, *args)
jake_wag = lambda *args: dog_wag(name, *args)
jake_woof = lambda *args: dog_woof(name, *args)

>>> jake_bark("Finn")
Jake the dog says 'Bark at you, Finn'

Both approaches serve the same function — they essentially attach some repetitive information to all function invocations.

Level 2

Now, we are getting to the point of my question. What if Jake talks to Finn very often, and I would like to make their conversation an entity of some sort? I can easily do that with functional approach:

target = "Finn"
jake_tell_finn = lambda *args: dog_tell(name, target, *args)
jake_bark_at_finn = lambda *args: dog_bark(name, target, *args)
jake_wag_at_finn = lambda *args: dog_wag(name, target, *args)

>> jake_bark_at_finn()
Jake the dog says 'Bark at you, Finn'

This made one more step as compared to the instantiation, and attached more information to function calls.

But what would be the correct OOP approach for this scenario? What if I continue this chain of adding partial state information to the class methods, effectively "instantiating the instances", "instantiating the instanciated instances" and so on?

To clarify the intent, I think of something like this:

>>> jake = Dog("Jake")
>>> jake_and_finn = jake.with_buddy("Finn")
>>> jake_and_finn.tell("dirty secrets")
Jake the dog tells dirty secrets to Finn

While I know how to implement this "as is", my concrete implementation lacks the scalability, abstractness and beauty. It hurts my feelings of beautiful, and I feel I lack the understanding of something important at the very basic theory behind this.

1

There are 1 best solutions below

2
On

If I understand you correctly, I think you are talking about an abstract question about graph.

jake and finn are two nodes in the graph, and their communication channel is the edge in this graph. So if we construct every "dog" and their communication channel as a big graph, we can use two class to describe these two instances.

One is Dog as node, and another is Tell as edge. We instantiate every dog as a Dog. Dog has a function receives one parameter to specify his buddy, and returns an instance of Tell as a directed edge(determined by your need, it can be undirected.)

Finally, we use the instance of Tell to communicate.

For example:

jake = Dog("jake")
finn = Dog("finn")
jake_and_finn_tell = jake.with_buddy(finn)
# jake_and_finn_tell is an instance of class Tell.
jake_and_finn_tell.tell("dirty secrets")