2016-07-27 69 views
3

现在我正在开发一个用于识别照片中的对象的类,并且此类由多个组件(类)组成。例如,这是一个使用抽象工厂模式的例子吗?

class PhotoRecognizer 
{ 
public: 
    int perform_recogniton() 
    { 
     pPreProcessing->do_preprocessing(); 
     pFeatureExtractor->do_feature_extraction(); 
     pClassifier->do_classification() 
    } 

    boost::shared_ptr<PreProcessing> pPreProcessing; 
    boost::shared_ptr<FeatureExtractor> pFeatureExtractor; 
    boost::shared_ptr<Classifier> pClassifier; 

} 

在这个例子中,当我们使用这个类来进行识别,我们调用其他类PreProcessingFeatureExtractorClassifier。正如你可以想象的那样,有很多不同的方法来实现每个类。例如,对于Classifier类,我们可以使用SVMClassfierNeuralNetworkClassifer,它是基本类Classifier的派生类。

class SVMClassifier: public Classifier 
{ 
public: 
    void do_classification(); 

}; 

因此,通过使用PhotoRecognizer类中的不同元素,我们可以创建不同种PhotoRecongnizer。现在,我正在构建一个基准,以了解如何将这些元素结合在一起以创建最佳PhotoRecognizer。一个解决方案,我能想到的就是用抽象工厂:

class MethodFactory 
{ 
public: 
     MethodFactory(){}; 
     boost::shared_ptr<PreProcessing> pPreProcessing; 
     boost::shared_ptr<FeatureExtractor> pFeatureExtractor; 
     boost::shared_ptr<Classifier> pClassifier; 

}; 
class Method1:public MethodFactory 
{ 
    public: 
    Method1():MethodFactory() 
    { 
      pPreProcessing.reset(new GaussianFiltering); 
      pFeatureExtractor.reset(new FFTStatictis); 
      pClassifier.reset(new SVMClassifier); 

     } 

}; 

class Method2:public MethodFactory 
{ 
    public: 
    Method1():MethodFactory() 
    { 
      pPreProcessing.reset(new MedianFiltering); 
      pFeatureExtractor.reset(new WaveletStatictis); 
      pClassifier.reset(new NearestNeighborClassifier); 

     } 

}; 



class PhotoRecognizer 
    { 
    public: 
     PhotoRecognizer(MethodFactory *p):pFactory(p) 
     { 
     } 
     int perform_recogniton() 
     { 
      pFactory->pPreProcessing->do_preprocessing(); 
      pFactory->pFeatureExtractor->do_feature_extraction(); 
      pFactory->pClassifier->do_classification() 
     } 

     MethodFactory *pFactory; 


    } 

所以当我用方法一进行照片的认可,我可以简单地做到以下几点:

Method1 med; 
PhotoRecognizer recogMethod1(&med); 
med.perform_recognition() 

更进一步,我甚至可以使类PhotoRecognizer更紧凑:

enum RecMethod 
{ 
    Method1, Method2 

}; 

class PhotoRecognizer 
{ 
public: 
    PhotoRecognizer(RecMethod) 
    { 
     switch(RecMethod) 
     { 
      case Method1: 
      pFactory.reset(new Method1()); 
      break; 
      ... 
     } 
    } 

    boost::shared_ptr<MethodFactory> pFactory; 

}; 

因此,这里是我的问题:是抽象工厂设计模式有充分理由在上述的情况呢?有其他解决方案吗?谢谢。

+0

这与C++无关,为什么标记在那里? – Ceros

+0

@Ceros演示代码在C++ – feelfree

+0

但你的问题是关于代码设计而不是C++ – Ceros

回答

2

由于经常没有最终的“正确”的方法来做到这一点,和答案很大程度上取决于项目将如何使用。所以如果只是为了快速测试,一次完成并且永不回头 - 如果这是你内心的愿望,那就继续使用枚举吧,没有人会阻止你。但是,如果您打算随着时间的推移扩展可能的方法,我会劝阻使用枚举的第二种方法。原因是:每次你想添加一个新的方法时,你必须改变PhotoRecognizer类,所以你必须阅读代码,记住它正在做什么,如果有人应该这样做 - 这将需要更多的时间。

与枚举的设计违反的固体(https://en.wikipedia.org/wiki/SOLID_(object-oriented_design))两个第一规则:

  1. 开闭原理(OCP):PhotoRecognizer类不能扩展(添加的新方法),而不其代码的修改。
  2. 单责任原则(SRP):PhotoRecognizer班级不仅承认照片,而且还充当方法的工厂。

你的第一种方法比较好,因为如果你需要定义另一个Method3你可以把它变成你的PhotoRecognizer,并用它在不改变类的代码:

//define Method3 somewhere 
Method3 med; 
PhotoRecognizer recogMethod3(&med); 
med.perform_recognition() 

我不喜欢关于你的方法,就是对于每一种可能的组合,你必须写一个班级(MethodX),这可能会导致很多无聊的工作。我会做到以下几点:

struct Method 
{ 
    boost::shared_ptr<PreProcessing> pPreProcessing; 
    boost::shared_ptr<FeatureExtractor> pFeatureExtractor; 
    boost::shared_ptr<Classifier> pClassifier; 
}; 

Method如为不同的算法插槽的集合,它在这里,因为可以很方便地通过处理/提取/分级这样。

人们可以使用一个工厂函数:

enum PreprocessingType {pType1, pType2, ...}; 
    enum FeatureExtractorType {feType1, feType2, ..}; 
    enum ClassifierType {cType1, cType2, ... }; 

    Method createMethod(PreprocessingType p, FeatureExtractionType fe, ClassifierType ct){ 
     Method result; 
     swith(p){ 
      pType1: result.pPreprocessing.reset(new Type1Preprocessing()); 
        break; 
      .... 
     } 
     //the same for the other two: fe and ct 
     .... 
     return result 
    } 

也许你会问:“可是怎么样OCP” - 你会是对的!必须更改createMethod以添加其他(新)类。这对你来说可能并不太舒服,你仍然可以手动创建一个Method对象,用新类初始化这些字段并将它传递给一个PhotoRecognizer构造器。

但随着C++,您在您的处置一个强大的工具 - 模板:

template < typename P, typename FE, typename C> 
    Method createMethod(){ 
     Method result; 
     result.pPrepricessing.reset(new P()); 
     result.pFeatureExtractor.reset(new FE()); 
     result.pClassifier.reset(new C()); 
     return result 
    } 

而且你可以自由选择你想要的任何组合,而无需修改代码:

//define P1, FE22, C2 somewhere 
Method medX=createMethod<P1, FE22, C2>(); 
PhotoRecognizer recogMethod3(&med); 
recogMethod3.perform_recognition() 

有是另一个问题:如果类PreProcessingA不能与类ClassifierB一起使用会怎么样?此前,如果没有类MethodAB没有人可以使用它,但现在这个错误是可能的。

为了解决这个问题,特征可以用:

template <class A, class B> 
struct Together{ 
    static const bool can_be_used=false; 

template <> 
struct Together<class PreprocessingA, class ClassifierA>{ 
    static const bool can_be_used=true; 
} 

template < typename P, typename FE, typename C> 
Method createMethod(){ 
    static_assert(Together<P,C>::can_be_used, "classes cannot be used together"); 
     Method result; 
     .... 
} 

结论

这种方法具有以下优点:

  1. SRP,即PhotoRecognizer - 只承认,Method - 仅捆绑算法部分和createMethod - 仅创建一个方法。
  2. OCP,即我们可以在不更改其他类/函数的代码的情况下添加新算法
  3. 由于性状,我们可以在编译时检测到部分算法的错误组合。
  4. 没有样板代码/没有代码重复。

PS:

你可能会说,为什么不划伤整个Method类?人们也可以使用:

template < typename P, typename FE, typename C> 
    PhotoRecognizer{ 
     P preprocessing; 
     FE featureExtractor; 
     C classifier; 
     ... 
    } 

    PhotoRecognizer<P1, FE22, C2> recog(); 
    recog.perform_recognition(); 

是的,这是真的。这种替代方案有一些优点和缺点,必须更多地了解项目才能做出正确的折衷。但默认情况下,我会采用更多SRP原则兼容方法将部分算法封装到Method类中。

0

除非你是重用MethodFactory,我提出以下建议:

struct Method1 { 
    using PreProcessing_t = GaussianFiltering; 
    using FeatureExtractor_t = FFTStatictis; 
    using Classifier_t = SVMClassifier; 
}; 

class PhotoRecognizer 
{ 
public: 
    template<typename Method> 
    PhotoRecognizer(Method tag) { 
     pPreProcessing.reset(new typename Method::PreProcessing_t()); 
     pFeatureExtractor.reset(new typename Method::FeatureExtractor_t()); 
     pClassifier.reset(new typename Method::Classifier_t()); 
    } 
}; 

用法:

PhotoRecognizer(Method1()); 
1

我已经在这里和那里实现了抽象工厂模式。在重新审视维护代码后,我一直对此决定表示遗憾。没有我能想到的情况,一个或多个工厂方法不会是一个更好的主意。因此,我最喜欢你的第二种方法。考虑按照ead的建议抛弃方法类。一旦您的测试完成,您将拥有一个或多个工厂方法,可以精确构建您想要的内容,最重要的是,您和其他人将能够在以后遵循代码。例如:

std::shared_ptr<PhotoRecognizer> CreateOptimizedPhotoRecognizer() 
{ 
    auto result = std::make_shared<PhotoRecognizer>(
     CreatePreProcessing(PreProcessingMethod::MedianFiltering), 
     CreateFeatureExtractor(FeatureExtractionMethod::WaveletStatictis), 
     CreateClassifier(ClassificationMethod::NearestNeighborClassifier) 
     ); 

    return result; 
} 

用你的工厂方法在这样的代码:

auto pPhotoRecognizer = CreateOptimizedPhotoRecognizer(); 

创建枚举如你所说。我知道,我知道,打开/关闭原则......如果将这些枚举保留在一个点上,就不会有问题使它们与工厂方法保持同步。首先枚举:

enum class PreProcessingMethod { MedianFiltering, FilteringTypeB }; 
enum class FeatureExtractionMethod { WaveletStatictis, FeatureExtractionTypeB }; 
enum class ClassificationMethod { NearestNeighborClassifier, SVMClassfier, NeuralNetworkClassifer }; 

这里有一个组件工厂方法的一个例子:

std::shared_ptr<PreProcessing> CreatePreProcessing(PreProcessingMethod method) 
{ 
    std::shared_ptr<PreProcessing> result; 

    switch (method) 
    { 
     case PreProcessingMethod::MedianFiltering: 
      result = std::make_shared<MedianFiltering>(); 
      break; 

     case PreProcessingMethod::FilteringTypeB: 
      result = std::make_shared<FilteringTypeB>(); 
      break; 

     default: 
      break; 
    } 

    return result; 
} 

为了确定您可能希望创建在所有运行一些自动化测试的算法的最佳组合组件的可能排列。做到这一点的一种方法可能是这样简单:

for (auto preProc = static_cast<PreProcessingMethod>(0); ; 
    preProc = static_cast<PreProcessingMethod>(static_cast<int>(preProc) + 1)) 
{ 
    auto pPreProcessing = CreatePreProcessing(preProc); 
    if (!pPreProcessing) 
     break; 

    for (auto feature = static_cast<FeatureExtractionMethod>(0); ; 
     feature = static_cast<FeatureExtractionMethod>(static_cast<int>(feature) + 1)) 
    { 
     auto pFeatureExtractor = CreateFeatureExtractor(feature); 
     if (!pFeatureExtractor) 
      break; 

     for (auto classifier = static_cast<ClassificationMethod>(0); ; 
      classifier = static_cast<ClassificationMethod>(static_cast<int>(classifier) + 1)) 
     { 
      auto pClassifier = CreateClassifier(classifier); 
      if (!pClassifier) 
       break; 

      { 
       auto pPhotoRecognizer = std::make_shared<PhotoRecognizer>(
        pPreProcessing, 
        pFeatureExtractor, 
        pClassifier 
        ); 

       auto testResults = TestRecognizer(pPhotoRecognizer); 
       PrintConfigurationAndResults(pPhotoRecognizer, testResults); 
      } 
     } 
    } 
}