2016-05-10 30 views
0

我是軟件設計方面的一名非專業人士。我面臨着一個「問題」,可能是通過一些我熟悉的技術/成語/模式來解決的。軟件設計:課程太多?

我有一個抽象的基類,基本上定義一個純粹的虛擬成員函數和其他一些。然後我有幾個類從這個派生出來,並覆蓋上述虛擬函數。我現在已經有六個這樣的班級,人數也在增長。這些類只有少數幾個數據成員(很少有,比如幾個雙打或者加上一個函數指針),它們主要因爲它們執行非常短的計算的方式而有所不同。我想知道這是否表明設計不好,而且更好地以其他方式處理。

如果合適,有人能指出我的相關設計模式或成語我應該知道。謝謝。

EDIT

爲了澄清的東西,抽象基類沒有任何數據成員。並非所有的派生類都有數據成員。我正在做的是將積分座標轉換爲類。給定的轉換隻需要幾個參數,有時還需要用戶提供的功能。

+0

如果代碼沒有被複制,那麼你是在正確的軌道上。 – Dialecticus

+0

抽象基類是否有任何數據成員? –

+1

同意@Dialecticus,但函數指針提高了我的眉毛。爲什麼使用函數指針?你能不能把它設計成給定子類的細節?另外,如果你還沒有,[看看'std :: function'](http://en.cppreference.com/w/cpp/utility/functional/function)。它可以簡化函數指針的使用。 – user4581301

回答

2

如果你的抽象基類沒有任何數據成員(如果它有一個純粹的虛擬方法,它似乎不應該),那麼確實有更好的模式。假設我們有這樣的代碼:

struct AbstractBase { 
    virtual double calc(double) = 0; 
    virtual ~AbstractBase() = default 
} 

現在,你必須從這個才能使用的東西動態別處繼承:

struct Derived : public AbstractBase { ... } 

void BaseUser(AbstractBase& ab) { ... }; 

一個不太耦合求解也只是寫你的類作爲功能對象和使用std::function

struct Derived { 
    double operator()(double x) { ... }; 
} 

void User(std::function<double(double)> f); 

User(Derived{}); // Calls user with Derived routine. 

這具有其他優點,例如,如果你的一些派生類實際上並不需要的狀態,那麼你可以把它們寫爲純功能,不過仍可通過他們周圍的std::functions。另一個好處是,你可以寫簡短的內部函數以及現在,因爲lambda是一個函數對象:

User([] (double x) { return 2*x; }); 

如果您需要更精確的控制比單個虛擬函數調用一個抽象基類是確定的,但是當接口中只有一個函數時,我至少會考慮查看函數對象。

我不會關心你必然會有多少派生對象。

+0

即使你的函數需要狀態 - 你可以將它們寫成lambda表達式,從上下文中捕獲它們在構建時需要的內容,並將它們作爲函數或lambda表達式傳遞! – davidbak

+0

是的,抽象基類沒有任何數據成員。而且,並非所有的派生類都有成員。我正在做的實際事情是將座標轉換爲一個整體作爲類。有些可能會收到來自用戶的功能。我將編輯OP來澄清這一點。 – booNlatoT

+0

@davidbak是的,但是如果OP的抽象基類具有狀態,那麼就不可能用這種設計來模擬。我的評論具體是關於基類中的狀態,而不是在實現中。 –

1

有句話說:DRY。 D on't R epeat Y我們自己。除了重寫純虛擬方法之外,如果有任何其他形式的重複代碼,那麼這可能是需要修訂的標誌。如果你有很多類,並且每個類都有獨特的功能,那就沒問題。

0

如果操作可以編寫,那麼你應該嘗試編寫對象以組成操作。一個班只應該真的做一件事,所以你不一定有問題。

+0

我會說一點。一堂課應該代表一件事。它可以做很多事情。 – user4581301

+0

你說得對,功能應該做一件事。 – dasPing

1

沒有看到一個例子就很難充分評論。一般來說,你有很多類,每個類只有一小部分(一組)操作,並不一定表示不好的設計。你應該儘可能地使用DRY(不要重複自己)和SOLID(見this wikipedia article)原則來建立類。

+0

感謝您的參考。 – booNlatoT

3

具有一個虛擬函數的抽象基類聽起來幾乎完全像包含lambda的std::function<>

例如:

#include <functional> 
#include <vector> 
#include <iostream> 

int main() 
{ 
    using op = std::function<int()>; 

    int x = 7; 
    int y = 5; 
    auto a = [x, y]() -> int { return x + y; }; 
    auto b = [x, y]() -> int { return x - y; }; 

    auto ops = std::vector<op> { a, b }; 
    for (const auto& o : ops) 
    { 
     std::cout << o() << std::endl; 
    } 
} 

最後,一個拉姆達是寫入捕獲(拷貝)的某些對象或對象的引用,並公開一個呼叫操作員的一類的僅簡寫形式。

std :: function是這種類的多態適配器。

使用lambdas可能會爲您節省一些打字費用。是否增加或減少代碼的語義表達(可以說更重要)是另一回事。

+0

你甚至不需要std ::函數 - 如果你堅持'auto'和'decltype',你可以使用「裸體」lambdas,我知道,如果你使用std :: function,它可以比std :: function更加緊湊和高效不需要它提供的一般性。 – davidbak

+0

@davidbak我假設OP需要一個多態接口,因爲他使用的是ABC。沒有示例代碼,這是不可能的。 –

+0

是的,示例代碼本來不錯。我明白你對多態接口的看法。 – davidbak