简体   繁体   English

使用C ++中的现有类进行代码重构

[英]Code refactoring with existing classes in C++

I code in C++ and since the interface keyword is not there in the language, I will use interface as a concept( a contract) here. 我用C ++编写代码,由于接口关键字不在语言中,我将在这里使用接口作为概念(合同)。

Suppose you have an interface say IBase, which has been implemented by dozens of classes. 假设你有一个接口说IBase,它已被几十个类实现。 Now you need to add another method in that interface IBase. 现在,您需要在该接口IBase中添加另一个方法。

What would be the approach to make that change in minimal way to solve the problem of overriding that method in all implementing classes? 以最小的方式进行更改以解决在所有实现类中重写该方法的问题的方法是什么?

One way could be adding the new method with a default implementation in the Ibase class and have the derived class that need it override it. 一种方法是在Ibase类中添加带有默认实现的新方法,并让需要它的派生类覆盖它。

In the above solution I feel like I am going wrong at two places, breaking the open closed principle by touching the interface and also breaking the contract by telling the user you can also call another method. 在上面的解决方案中,我觉得我在两个地方出错了,通过触摸界面打破了开放的封闭原则,并通过告诉用户你也可以调用另一种方法来打破合同。

Another thing that strikes me here is , I am adding a method to the base and only one derived class is overriding it , which basically means that the other classes don't need that method. 在这里让我感到震惊的另一件事是,我在基础上添加了一个方法,只有一个派生类覆盖它,这基本上意味着其他类不需要该方法。

So that brings me to the another solution, where the class which needs this method would inherit from another class which provides this feature or use composition to use the feature of another class. 这样我就可以使用另一个解决方案,其中需要此方法的类将继承自另一个提供此功能的类或使用组合来使用另一个类的功能。

With the above solution I hit another problem, how would the client access the new functionality of the derived class through the Ibase interface. 通过上面的解决方案我遇到了另一个问题,客户端如何通过Ibase接口访问派生类的新功能。 Dynamic dispatch in C++ would only work if the function is present in the base class. 只有函数存在于基类中时,C ++中的动态调度才有效。

Can someone please help! 有人可以帮忙!

UPDATE UPDATE

I just coded up my understanding based on the comments below, but now the client code looks messy, it have to know which interface to use for extra functionality. 我只是根据下面的评论编写了我的理解,但现在客户端代码看起来很乱,它必须知道哪个接口用于额外的功能。 Should we abstract the code below using factory/ abstract factory ? 我们应该使用工厂/抽象工厂抽象下面的代码吗?

#include <iostream>

using namespace std;


class IBase {
    public:
     virtual void test() = 0; 
};

class INewInterface {
    public:
     virtual void newMethod() = 0; 
};

class Derived : public IBase {
    public:
    virtual void test () {
        cout<< " Derived  " << endl;
    }
};

class DerivedSecond: public IBase, public INewInterface {
    public:
    virtual void test() {
        cout<< " Derived Second " << endl;
    }

    void newMethod( ) {
        cout<< " newMethod " << endl;
    }
};

int main( int argc, char ** argv ) {

    // Client code
    //Client needs to cast to two different interfaces to get access to the new functionality.
    // Probably should use a factory here 
    INewInterface * pNewInterfacePtr = dynamic_cast< INewInterface * > ( new DerivedSecond );
    IBase *         pIbasePtr        = dynamic_cast< IBase * > ( new DerivedSecond );

    pIbasePtr->test();
    pNewInterfacePtr->newMethod();
    return 0;
}

It is difficult to really know what will suit your situation because you evidently have a working system which may or may not benefit from anything we might suggest. 很难真正了解什么适合您的情况,因为您显然拥有一个可能会或可能不会从我们建议的任何事情中受益的工作系统。

However I have produced an example of how you can manage multiple subtypes with separate and/or overlapping responsibilities. 不过,我产生了一个如何管理与单独和/或职责重叠多个亚型的例子。 This approach is to have a master container holding ownership of all the objects using a std::unique_ptr to ensure they all get deleted and we have no memory leaks. 这种方法是让一个容器使用std::unique_ptr保存所有对象的所有权,以确保它们都被删除,并且我们没有内存泄漏。

In addition to that master container we have separate containers for different views . 除了 容器之外 ,我们还有不同视图的独立容器。 Each view holds references (raw pointers) to those elements with a specific responsibility not shared with other types. 每个视图都包含对那些具有不与其他类型共享的特定职责的元素的引用(原始指针)。

Perhaps it may be of some use in your situation, perhaps not: 也许它可能在你的情况下有用,也许不是:

#include <vector>
#include <memory>
#include <iostream>

class Role
{
public:
    virtual ~Role() {}
};

class RoleOne
: public virtual Role
{
public:
    virtual void do_role_one_stuff() = 0;
};

class RoleTwo
: public virtual Role
{
public:
    virtual void do_role_two_stuff() = 0;
};

class ItemA
: public RoleOne
{
public:
    void do_role_one_stuff() override { std::cout << "Item A in Role One\n"; }
};

class ItemB
: public RoleOne
, public RoleTwo
{
public:
    void do_role_one_stuff() override { std::cout << "Item B in Role One\n"; }
    void do_role_two_stuff() override { std::cout << "Item B in Role Two\n"; }
};

class ItemC
: public RoleTwo
{
public:
    void do_role_two_stuff() override { std::cout << "Item C in Role Two\n"; }
};

class Resources
{
    // unique_ptr ensures deletion (no memory leaks)
    std::vector<std::unique_ptr<Role>> all; // owning container

    // raw 'access' pointers share access (no need to share ownership)
    std::vector<RoleOne*> ones; // alternate 'view' (no ownership)
    std::vector<RoleTwo*> twos; // alternate 'view' (no ownership)

public:
    void add_item(Role* item)
    {
        // manage ALL items life-spans here
        all.emplace_back(item);

        // add one-centric items to the one-centric view
        if(auto one = dynamic_cast<RoleOne*>(item))
            ones.emplace_back(one);

        // add two-centric items to the two-centric view
        if(auto two = dynamic_cast<RoleTwo*>(item))
            twos.emplace_back(two);
    }

    void do_business()
    {
        // ItemA and ItemB types do this kind of business
        std::cout << "\nDoing role one business:\n";
        for(auto role: ones)
            role->do_role_one_stuff();

        // ItemB and ItemC types do this kind of business
        std::cout << "\nDoing role two business:\n";
        for(auto role: twos)
            role->do_role_two_stuff();
    }
};

int main()
{
    Resources res;

    res.add_item(new ItemA);
    res.add_item(new ItemB);
    res.add_item(new ItemC);
    res.add_item(new ItemB);
    res.add_item(new ItemA);
    res.add_item(new ItemC);

    res.do_business();
}

Output: 输出:

Doing role one business:
Item A in Role One
Item B in Role One
Item B in Role One
Item A in Role One

Doing role two business:
Item B in Role Two
Item C in Role Two
Item B in Role Two
Item C in Role Two

Remember, principles are not rules. 请记住,原则不是规则。 You will find sometimes that you have to break some principle in the real worl and it will be a good decision if you understand what you are doing. 有时你会发现你必须在真正的世界中打破一些原则,如果你明白自己在做什么,这将是一个很好的决定。 Then, the option of creating a new dummy method in IBase could be your solution. 然后,在IBase中创建新的虚拟方法的选项可能是您的解决方案。

If you do not want to break the actual interface, I would use composition and delegation. 如果你不想打破实际的界面,我会使用组合和委托。 But I do not know your issue. 但我不知道你的问题。 Maybe my solution does not fit in your case. 也许我的解决方案不适合你的情况。

class IBase {
public:
    virtual void test() = 0;
};
class INewIfc {
protected:
    IBase* ifc;
public:
    INewIfc(IBase* _ifc) : ifc(_ifc) {}
    ~INewIfc() {}
    virtual void newMethod() = 0;
    IBase* getIfc() {return ifc;}
}

Then you create your concrete classes from IBase (what you had) or use composition of IBase and the new interface to create new ones without affecting the previous classes. 然后,您从IBase(您拥有的)创建具体类,或使用IBase的组合和新接口创建新的接口,而不会影响以前的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM