A
A
Alexander2012-09-26 16:37:03
OOP
Alexander, 2012-09-26 16:37:03

Matrix polymorphism?

Colleagues, good afternoon.

An architectural problem arose, which I did not master right away.
So, the application has some kind of data processing that depends on two parameters (m, n). Depending on the combination of parameters, some actions can be performed, sometimes the same, for different parameters, sometimes different. In total, we have a matrix MxN, where in each cell of which there is a certain action A. The ratio of the number of options for parameters and actions is approximately the following M~=N~=A/3 .

The old implementation is made in the form of a giant switch by M with nested switches by N (only 230 lines in Delphi).

A typical piece looks something like this (in pseudocode)

case m_5:
{
  switch(n)
  case (n3,n5,n6,n18): {какой-то небольшой код}
  case (n4,n6,n8,n9): {какой-то небольшой код}
  default: {какой-то небольшой код}
}


Now this code is being ported to C++ and I wanted to make it a beautiful set of understandable classes. At first I wanted to make classes for each N with the necessary virtual methods for M. But then there is a lot of code duplication, because for a given N, the actions for several M are necessarily the same. The same is true if you change M to N. If you make a set of classes for A, parametrizing the factory with values ​​(m, n), then firstly you get a lot of classes A, and secondly, readability is greatly lost.

Now I am inclined to repeat everything as it is, but I will be glad if you tell me a breakthrough idea.

Answer the question

In order to leave comments, you need to log in

8 answer(s)
R
Ramires, 2012-09-26
@Ramires

It seems that in McConnell's book "Perfect Code" there was a chapter about tabular methods. Read it (pages 20-30 in total), maybe it will help you. The simplest option that I came up with right away:
create a table, in each cell of which there will be a callback to the desired function.
Perhaps I'm wrong, and you definitely need classes.

T
TheHorse, 2012-09-26
@TheHorse

We implement all Ai functions:
function() A1() {};
function() A2() {};
function() A3() {};
...
Next, we take pointers to all these functions and write them to Pi;
Next, we take the MxN matrix and write the index of the desired function into the corresponding i, j.
Then, for i, j, we take a pointer to the desired function and execute it.
Something like this.
It can be implemented without pointers by making one function A(i) that performs the desired function for the required i.
PS Perhaps, I firmly believe that the functions are similar, and differ only in parameters, which can improve the implementation.

M
motl, 2012-09-26
@motl

If M is a set of classes and N is a set of classes, then this is a classical visitor pattern.

R
rtorsten, 2012-09-26
@rtorsten

I would probably do something like this:

typedef void (*HandlerFunction)(<params>); // or typedef std::function<void (params)> HandlerFunction;

struct HandlerKey { int m; int n; } // реализуем так же операторы для сравнения, конструктор ...
typedef std::map<HandlerKey, HandlerFunction> HandlerMap;

void init(HandlerMap & m)
{
    m[HandlerKey(m1, n2)] = HandlerFunction1;
    m[HandlerKey(m1, n2)] = HandlerFunction2;
    m[HandlerKey(m2, n1)] = HandlerFunction1;
    m[HandlerKey(m2, n2)] = HandlerFunction3;
    ...
}

void process(const HandlerKey & key, <params>)
{
    HandlerMap::iterator it = handlerMap.find(key);
    if (it == handlerMap.end()) 
         std::runtime_error("No handler for key " + key.toString());
    it->second(<params>);
}

I made a callback style, but if OOP is actively used, then it's probably better to do something like a functor, because most likely handlers use similar functionality that can be shared through base classes.

E
EaS, 2012-09-27
@EaS

The assignment can be done as a function with a variable number of arguments, I think:
init(ValueType value, int line, int column, ...); // prototype
init(value, line, column1, column2, column3); // this is what the call will look like
then there will be no m*n assignments.
If you use a fresh version of gcc, then the same can be done through variadic templates (I really don’t know why :) )

B
bfDeveloper, 2012-09-29
@bfDeveloper

You have described one of the best solutions: M classes of N methods. Duplication is easy to avoid by implementing everything once, and in other methods just call the already implemented ones. Your goal for OOP style is googling for the phrase "multiple polymorphism". It seems that Meers offered a solution in the form of map<key, function>, which simply writes pointers to functions, as you were already offered above.

A
Alexander, 2012-10-01
@ncix

Дублирования легко избежать, реализовав всё по 1 разу, а в остальных методах просто вызывать уже реализованные

The specifics of the task is such that the implementation of each action A is no more than 1 line anyway, so recalls will create a bunch of wrappers over very modest content. That is why this option is not very suitable.

N
NTSky, 2013-06-14
@NTSky

Thanks for your question. He made me think about your task, and as a result, I wrote my first article on Habr, for which I received an invite. habrahabr.ru/post/183332/ I hope it will be useful to you.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question