工厂方法设计模式和桥接模式有什么区别?

5

大家好,

请问有人可以解释一下工厂方法模式和桥接模式的区别吗?因为我理解,这两个设计模式都用于分离界面和实现:

将抽象与实现解耦。抽象将客户端代码与实现分开。因此,实现可以更改而不影响客户端代码,并且在实现更改时无需编译客户端代码。

工厂方法:

没有工厂方法的问题:

有些情况下,我们有一个包含一些类以实现客户业务逻辑的库。从客户应用程序中,我们创建库类的对象来完成任务。

但是,有时基于客户要求来纳入其他功能,我们可能需要在库中添加其他类。然后我们需要在客户端应用程序中创建新类的对象。

因此,每次在库方面进行新改变时,客户端都需要在其端进行一些相应的更改并重新编译代码。

使用工厂方法:

为了避免这个问题,我们使用工厂方法将对象创建与客户端应用程序解耦。客户端只需要调用库的工厂方法即可,无需担心对象创建的实际实现。

因此,我们创建工厂方法来创建对象并将其移动到单独的实现文件中。 现在,实现文件是唯一需要了解派生类的文件。因此,如果对任何派生类进行更改或添加任何新类,则只需重新编译实现文件。使用工厂的每个人都只关心接口,这应该在应用程序的整个生命周期中保持一致。

客户端应用程序交互-->工厂方法和调用-->实现

如果我采用下面的示例程序,在添加任何类或更改类之后,只需要重新编译Vehicle.cpp文件就足够了吗? 另外,在创建工厂方法时,我们是否使用静态方法?

Vehicle.h

#include <iostream> 
using namespace std; 

enum VehicleType { 
    VT_TwoWheeler,    VT_ThreeWheeler,    VT_FourWheeler 
}; 

// Library classes 
class Vehicle { 
public: 
    virtual void printVehicle() = 0; 
    static Vehicle* Create(VehicleType type); 
}; 
class TwoWheeler : public Vehicle { 
public: 
    void printVehicle() { 
        cout << "I am two wheeler" << endl; 
    } 
}; 
class ThreeWheeler : public Vehicle { 
public: 
    void printVehicle() { 
        cout << "I am three wheeler" << endl; 
    } 
}; 
class FourWheeler : public Vehicle { 
    public: 
    void printVehicle() { 
        cout << "I am four wheeler" << endl; 
    } 
}; 

Vehicle.cpp (实现文件)

// Factory method to create objects of different types. 
// Change is required only in this function to create a new object type 
Vehicle* Vehicle::Create(VehicleType type) { 
    if (type == VT_TwoWheeler) 
        return new TwoWheeler(); 
    else if (type == VT_ThreeWheeler) 
        return new ThreeWheeler(); 
    else if (type == VT_FourWheeler) 
        return new FourWheeler(); 
    else return NULL; 
} 

Client.h文件

// Client class 
class Client { 
public: 

    // Client doesn't explicitly create objects 
    // but passes type to factory method "Create()" 
    Client() 
    { 
        VehicleType type = VT_ThreeWheeler; 
        pVehicle = Vehicle::Create(type); 
    } 
    ~Client() { 
        if (pVehicle) { 
            delete[] pVehicle; 
            pVehicle = NULL; 
        } 
    } 
    Vehicle* getVehicle()  { 
        return pVehicle; 
    } 

private: 
    Vehicle *pVehicle; 
}; 

// Driver program 

int main() { 
    Client *pClient = new Client(); 
    Vehicle * pVehicle = pClient->getVehicle(); 
    pVehicle->printVehicle(); 
    return 0; 
}

请提供您对此的想法。
提前致谢。

桥接模式是一种结构型模式。它将接口与实现分离。 工厂方法是一种创建型模式。它负责创建对象实例。 - mfnx
“只重新编译Vehicle.cpp文件就足够了吗?” 你试过了吗?我认为这已经足够了。 - mfnx
我们使用静态方法吗? 是的,否则你需要一个车辆实例来调用Create。 - mfnx
这里使用 delete[] 是错误的。安全的做法是从 Create 返回 unique_ptr - Davis Herring
1个回答

1
"Factory Method" 是关于创建对象的灵活性,可以在创建基础代码时预留创建新对象的空间,而这些对象在创建时是未知的。例如,假设您正在为要与项目集成的IO卡设计接口包装器。您开始使用Decklink卡和Bluefish卡。稍后,如果您想添加Matrox卡而不修改任何过去的代码,则可以使用工厂模式。
"Bridge Pattern" 更多地涉及将接口与实现分离(隐藏复杂代码)。
在您的代码中有一个注意事项:假设您想添加VT_FiveWheeler - 您必须修改Vehicle.h以添加枚举VehicleType(听起来很糟糕)。Create方法可以是静态的,也可以是一个简单的辅助函数,如下所示: "
namespace Vehicle {
    class Vehicle {
    public:
        virtual void printVehicle() = 0;
    };
}

namespace Vehicle {
    namespace TwoWheeler {

        class TwoWheeler : public Vehicle {
        public:
            void printVehicle() {
                cout << "I am two wheeler" << endl;
            }
        };
        shared_ptr<Vehicle> create() { return make_shared<TwoWheeler>(); }
    }
}

namespace Vehicle {
    namespace ThreeWheeler {
        class ThreeWheeler : public Vehicle {
        public:
            void printVehicle() {
                cout << "I am three wheeler" << endl;
            }
        };
        shared_ptr<Vehicle> create() { return make_shared<ThreeWheeler>(); }
    }
}

namespace Vehicle {
    class factory
    {
        typedef function<shared_ptr<Vehicle>(void) > func_t;

    public:

        //register a vehicle
        void register_vehicle(const string& name, func_t func)
        {
            registered_vehicles_.insert(make_pair(name, func));
        }

        //to create a vehicle
        shared_ptr<Vehicle> create_vehicle(string name)
        {
            return registered_vehicles_[name]();
        }

        map<string, func_t> get() { return registered_vehicles_;  }

    private:
        map<string, func_t> registered_vehicles_;
    };
}

Vehicle::factory my_vehicle_factory;


int main() {

    //register the vehicle in the factory first
    my_vehicle_factory.register_vehicle("TwoWheeler", Vehicle::TwoWheeler::create);

    auto p1 = my_vehicle_factory.create_vehicle("TwoWheeler");
    auto p2 = my_vehicle_factory.create_vehicle("TwoWheeler");

    // A new vehicle has arrived, so first registered it
    my_vehicle_factory.register_vehicle("ThreeWheeler", Vehicle::ThreeWheeler::create);
    auto p3 = my_vehicle_factory.create_vehicle("ThreeWheeler");


    p1->printVehicle();
    p2->printVehicle();
    p3->printVehicle();
}

非常感谢您的详细解释。 作为一个初学者,很高兴能看到一些高级的C++概念,比如命名空间、智能指针(shared_ptr)、STL(映射)。 如果您能解释一下它们在这个上下文中的必要性以及它们是如何工作的,那就太好了。 在这里,代码中没有命名冲突。那么使用“命名空间”的必要性是什么? 使用“命名空间”的原因是什么? 为什么我们要使用shared_ptr? 提供共享所有权的必要性是什么? - John Paul Coder
还有为什么我们要在类外部声明shared_ptr呢? "shared_ptr<Vehicle> create() { return make_shared<TwoWheeler>(); }" 还有为什么我们要在命名空间之外声明“Vehicle::factory my_vehicle_factory;”呢? 最后,为什么我们在这里使用map? - John Paul Coder
所有车辆对象的构建都是相互独立的,为了在创建过程中保持对称性,它们使用单个帮助函数create()进行创建,但通过命名空间进行分隔以避免冲突。 - seccpur
这里使用std::map来简化新车辆的注册。使用指针实现多态性,shared_ptr优于原始指针。您可以使用静态成员函数代替辅助函数。本想展示桥接模式的工作示例,但回答被截断了。 - seccpur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接