How to merge the same method of different classes in the clean way

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Daniel Castro
    New Member
    • Dec 2011
    • 3

    #1

    How to merge the same method of different classes in the clean way

    Hi everybody..

    I'm developing an application using Qt, but I need some about how to avoid code repeat without sharing the private members of my class.
    I am working with some collections (QHash) of project-specific structs and I created a polymorphic class to manage those collections. A derived class also manage some UI components (shared through pointers or references) so it can automatically represent those structs in the UI.. It's something like this:

    Main class

    Code:
    class ArmyEditor : public QMainWindow
    {
    //.... some specific functions ...
    
    private:
        Ui::ArmyEditor *ui;
    
        // Specific structs
        QHash<QString, GameCategory> categories;
        QHash<QString, Column> columns;
        QHash<QString, GameProperty> properties;
        QHash<QString, UnitOption> commonOptions;
        QHash<QString, UnitOption> inheritedOptions;
        QHash<QString, GameItem> items;
        QHash<QString, GameItem> inheritedItems;
        QHash<QString, GlobalText> globalTexts;
        QHash<QString, GlobalText> inheritedGlobalTexts;
        QHash<QString, Unit> units;
    };
    Base class for collection managing..

    Code:
    class StructManager : public QObject { // Base class
        Q_OBJECT
    public:
    
        explicit StructManager(QWidget* parent = 0);
    
         virtual bool addCategory(const GameCategory &category, const QString &key);
         // ..More virtual functions...
    protected:
        QWidget *parent;
        QHash<QString, GameCategory> *categories; // Shared members
        QHash<QString, Column> *columns;
        QHash<QString, GameProperty> *properties;
        QHash<QString, UnitOption> *commonOptions;
        QHash<QString, GameItem> *commonItems;
        QHash<QString, GlobalText> *globalTexts;
        QHash<QString, Unit> *units;component
        // There are more here
    };
    Code:
    bool StructManager::addCategory(const GameCategory &category, const QString &key) {
        if(categories && GameStructs::isValidKey(key, parent)) {
            if(!categories->contains(key)) {
                categories->insert(key, category);
                emit categoryAdded(category, key);
                return true;
            } else {
                if(showMessages)
                    QMessageBox::critical(parent, tr("Can not add the category"), tr("The category can not be added because its ID (%1) already exists").arg(key));
                return false;
            }
        } else
            return false;
    }
    Derived class for UI management and so on

    Code:
    class StructEditor : public StructManager
    {
        Q_OBJECT
    public:
    
        StructEditor(QWidget* parent = 0);
        virtual bool addCategory(const GameCategory &category, const QString &key);
        // ...More overriden functions...
    
    protected:
        QTreeWidget *catList; // More shared items (there are get/set methods in the class)
        QListWidget *colList;
        QTreeWidget *propList;
        QTreeWidget *optList;
        QListWidget *optActionList;
        QTreeWidget *itemList;
        QListWidget *itemActionList;
        QTableWidget *globalTextsGrid;
        QTreeWidget *unitTree;
        QComboBox *optCategory;
        QComboBox *itemCategory;
        QComboBox *unitCategory;
        QComboBox *optAmountColumn;
        QComboBox *optSetColumn;
    };
    Code snippet

    Code:
    bool StructEditor::addCategory(const GameCategory &category, const QString &key) {
        if(catList && StructManager::addCategory(category, key)) {
            QStringList cells;
            cells << QString(category.getName());
            QTreeWidgetItem* item = new QTreeWidgetItem(cells);
            item->setData(0, Qt::UserRole, key);
            catList->topLevelItem(category.type)->addChild(item);
            catList->expandItem(catList->topLevelItem(category.type));
    
            if(category.type == GameCategory::CategoryOfOptions) {
                if(optList && optCategory) {
                    item = new QTreeWidgetItem(cells);
                    item->setData(0, Qt::UserRole, key);
                    optList->addTopLevelItem(item);
                    optCategory->addItem(category.getName(), key);
                }
            } else if(category.type == GameCategory::CategoryOfItems) {
                if(itemList && itemCategory) {
                    item = new QTreeWidgetItem(cells);
                    item->setData(0, Qt::UserRole, key);
                    itemList->addTopLevelItem(item);
                    itemCategory->addItem(category.getName(), key);
                }
            } else if(unitCategory && unitTree && category.type == GameCategory::CategoryOfUnits) {
                item = new QTreeWidgetItem(cells);
                item->setData(0, Qt::UserRole, QVariant(key));
                unitTree->addTopLevelItem(item);
                unitCategory->addItem(category.getName(), key);
            }
            return true;
        } else
            return false;
    }
    And I share some UI members in the constructor of the MainWindow class..

    Code:
    ArmyEditor::ArmyEditor(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::ArmyEditor)
    {
        ui->setupUi(this);
        
        // Setup Army Struct Manager
    
        armyManager = new StructEditor(this);
    
        armyManager->setCatList(ui->catList);
        armyManager->setOptList(ui->optList);
        armyManager->setOptActionList(ui->optActionList);
        armyManager->setItemList(ui->itemList);
        armyManager->setItemActionList(ui->itemActionList);
        armyManager->setGlobalTextsGrid(ui->globalTextsGrid);
        armyManager->setUnitTree(ui->unitTree);
        armyManager->setOptCategory(ui->optCategory);
        armyManager->setItemCategory(ui->itemCategory);
        armyManager->setUnitCategory(ui->unitCategory);
        armyManager->setOptAmountColumn(ui->optAmountColumn);
        armyManager->setOptSetColumn(ui->optSetColumn);
        armyManager->setCategories(&categories);
        armyManager->setOptions(&commonOptions);
        armyManager->setItems(&items);
        armyManager->setGlobalTexts(&globalTexts);
    
        //.. some other code ..
    };
    I call the functions from the StructEditor class when I need to add a new Category or something like that..
    My project consists of three applications, those of which use almost the same methods for managing these structs, so I decided to use a class with the methods to add, update, remove and represent the structs in the UI sharing some member pointers with the MainWindow class. But now I'm thinking it is a bit dirty and I should not share these members because the MainWindow loses control over them. I was thinking I could create the collections of my structs in the Base class and make a method so I can read (securely) members of those collections in the MainWindow class, but my problem is with UI members. I could use signals and manage those members directly in the MainWindow class, but then I would have to duplicate a lot of code and it would complicate (a bit) the code changes, which is the main reason I decided to unify those methods in a class.

    So, my question is: Is there any way to 'unify' those methods without having to share members or using ugly global variables? I would like to have those methods in a separated file.

    Thanks and greetings!
  • weaknessforcats
    Recognized Expert Expert
    • Mar 2007
    • 9214

    #2
    You are aware that there are no classes in C++?

    All you have are structs to corral members. That means your structs need constructors, destructors, methods, etc.

    The class was added to make C++ look like it as object-oriented. But underneath the class is implemented as a struct.

    The only dfference between a class and a struct is the default access. A struct is public. A class is private.

    Also, there should be no passing pointers around a C++ program. You have to use handles (managed pointers for this). Otherwise there is simply no way to be sure that the pointer contains a valid address.

    How can you have a code repeat trying to share a private member of a class when rivate members are not sharable. Doing so breaks encapsulation and data hiding and leads to spaghetti code. Could you post an example of this?

    Comment

    • Daniel Castro
      New Member
      • Dec 2011
      • 3

      #3
      Sharing private members was a way to avoid code repeat. If a hadn't "bool StructEditor::a ddCategory(cons t GameCategory &category, const QString &key)", I would have to create that method in each class I want to use. This method uses some pointers like catList, which I set in ArmyEditor constructor.

      Someone gave me an option using templates (in a new base class, an then ArmyEditor should inherit from it), and it seems to be the cleanest way to achieve what I'm trying to do.

      I can post the entire code if it is needed, but it is too large and you probably will just ignore it.

      Greetings!

      Comment

      • weaknessforcats
        Recognized Expert Expert
        • Mar 2007
        • 9214

        #4
        It may be there is a confusion between the interface and the implementation. Classes that are missing methods may compensate by using Visitors and Decorators.

        I wrote an article about Visitors in the C/C++ Insights forum.

        True, I may not read your entire code but your Class Diagram would be interesting.

        Comment

        • Daniel Castro
          New Member
          • Dec 2011
          • 3

          #5
          Probably it has terrible design flaws, but I'm trying to correct them. Here is the link to the entire project: http://code.google.com/p/yalb/downlo...r.zip&can=2&q=

          I also attached some files missing to the project actually compile,.

          I will read your article.

          Thanks and greetings!
          Attached Files

          Comment

          Working...