1

I have a base Abstract class Function and child classes ExpFunction, LogFunction, and CubicFunction. I have Main class as below:

class Main:
    """
    Main class to run the program
    """

    def __init__(self):
        self.f_1 = ExpFunction("Exponential", 0.0, 1.0, 3.0, -5.0, 0)
        self.f_2 = LogFunction("Logarithmic", 0.0001, 0.05, 3.0, 10.0, 0.001)
        self.f_3 = CubicFunction("f3", 1.0, 2.0, 2.0, -3.0, 4.0, -5.0, 0.0001)

    def _run(self):
        """
        Run the program
        """
        print("Starting the program...\n\n")
        self.f_1.print_function()
        self.f_1.find_roots()

        self.f_2.print_function()
        self.f_2.find_roots()

        self.f_3.print_function()
        self.f_3.find_roots()

        print("Program finished.")

    def __call__(self, *args, **kwds):
        self._run()


if __name__ == "__main__":
    main = Main()
    main()

I have drawn the following diagram: enter image description here

enter image description here

Is this correct diagram?

Christophe
  • 68,716
  • 7
  • 72
  • 138
Alijonov
  • 33
  • 6
  • Take a look at this to create a UML diagram automatically from source code: [What's the best way to generate a UML diagram from Python source code?](https://stackoverflow.com/questions/260165/whats-the-best-way-to-generate-a-uml-diagram-from-python-source-code) – Jorge Luis Mar 25 '23 at 18:10
  • @JorgeLuis This is a good idea. However tools like pylint also have a number of shortcommings where some associations are not correctly detected. In this particular case, it may not correctly detect that f_1, f_2 and f_3 are in reality meant to be Function, even if the instances are more specialized (which is also btw the weakness in OP's diagram) – Christophe Mar 25 '23 at 18:26
  • Where in the question do I find an abstract class at all? – qwerty_so Mar 26 '23 at 11:43

1 Answers1

1

Abstract classes and operations

We cannot say if Function and its 3 specialization are correct, since we don't have the corresponding code. But if it is abstract, it should be shown as such in the diagram (italic class name), and so should be the abstract operations (aka member functions in python) that you have explicitly defined in each of the 3 specialization.

Is this the real design?

In your code snippet, Main has f_1, f_2 and f_3. Your diagram says that these are always LogFunction, ExpFunction and CubicFunction. Considering the current code, this is not wrong. Here the diagram (simplified version) automatically generated by pyreverse (note that the pyreverse layout is not so nice as yours, and it also doesn't show the abstract items in italic):

enter image description here

However, nothing prevents you from changing f_1, f_2 and f_3 to be other kinds of Function, for example by adding the following reassignment in at the end of _run():

    self.f_2 = CubicFunction(...)
    self.f_2.print_function()

You could also chose to initialize them differently, because you use only their common abstract interface. You could therefore decide to show Main associated with 3 Function, and leave it to the runtime to decide which of the subclass to instantiate:

enter image description here

This diagram would also be correct. Moreover it corresponds to a more powerful design that better use the abstraction and polymorphism that your abstract class allows.

Since your code does not give a type hint and since python is dynamically typed, we cannot tell which of the two alternative design is the one you wanted. Only you know what you intended to do.

To generate the second diagram with pyreverese, I updated the initialization to use type hints; this makes the design intent more explicit in the code:

def __init__(self):
    self.f_1:Function = ExpFunction()
    self.f_2:Function = LogFunction()
    self.f_3:Function = CubicFunction()

As a side remark, feel free to remove the shared aggregation (hollow diamond): UML does not define any semantic for shared aggregation (since more than 20 years!) : a simple association would express exactly the same thing.

Christophe
  • 68,716
  • 7
  • 72
  • 138