Kiedy funkcje powinny być funkcjami składowymi?

Mam kolegę z mojej firmy, dla którego mam wiele szacunku, ale po prostu nie mogę zrozumieć jednego z jego preferowanych stylów pisania kodu w C ++.

Na przykład, biorąc pod uwagę, że istnieje klasa A, napisze globalne funkcje typu:

void foo( A *ptrToA ){}

lub:

void bar( const A &refToA ){}

Mój pierwszy instynkt po zobaczeniu takich globalnych funkcji brzmi: „Dlaczego nie są członkami A?” Będzie nalegał, aby było to zgodne z zaleceniami dotyczącymi dobrych praktyk w C ++, ponieważ foo i bar mogą wykonywać wszystko, czego potrzebują do wykonania, używając publicznego interfejsu A. Na przykład, będzie twierdził, że jest to całkowicie spójne z Scottem Meyersem Efektywne zalecenia C ++. Trudno mi to pogodzić z punktem 19 tej książki, który zasadniczo mówi, że wszystko powinno być funkcją składową z kilkoma wyjątkami (operator << i operator >> oraz funkcje, które wymagają dynamicznej konwersji typu). Co więcej, podczas gdy zgadzam się, że funkcje mogą robić to, co muszą zrobić z publicznym interfejsem A, moim zdaniem, jest to w dużej mierze rezultat pisania przez ludzi klas, które pobierają i ustawiają dla każdego elementu danych klasy A. Tak więc publiczny interfejs, A jest zbyt gloryfikowaną strukturą i na pewno możesz zrobić wszystko z interfejsem publicznym. Osobiście uważam, że nie należy tego wykorzystywać, myślę, że powinno się to zniechęcić.

Oczywiście jest to możliwe tylko w języku takim jak C ++, który nie jest czysty zorientowany obiektowo, więc sądzę, że jednym ze sposobów na to jest to, że mój kolega nie faworyzuje czysto obiektowego podejścia do projektowania oprogramowania. Czy ktoś zna jakąkolwiek literaturę, która popiera to stanowisko jako najlepszą praktykę? A może ktoś się z tym zgadza i może wyjaśnić mi to w inny sposób niż mój kolega, aby zobaczyć światło? Czy może wszyscy zgadzają się z moim obecnym poczuciem, że to po prostu nie ma sensu?

Edytować: Podam lepszy przykład kodu.

class Car
{
    Wheel frontLeft;
    Wheel frontRight;
    Wheel rearLeft;
    Wheel rearRight;
    Wheel spareInTrunk;

public:
    void wheelsOnCar( list< Wheel > &wheels )
    {
        wheels.push_back( frontLeft );
        wheels.push_back( frontRight);
        wheels.push_back( rearLeft);
        wheels.push_back( rearRight);
    }
    const Wheel & getSpare(){ return spareInTrunk; }
    void setSpare( const Wheel &newSpare ){ spareInTrunk = newSpare; }
    // There are getters and setters for the other wheels too,
    //but they aren't important for this example
};

Wtedy zobaczę taką funkcję:

void wheelsRelatedToCar( Car *aCar, list< Wheel > &wheels )
{
    aCar->wheelsOnCar( wheels );
    wheels.push_back( aCar->getSpare() );
}

To jest prawdziwy przykład z nazwami klas i funkcji zmienionych oczywiście. Dlaczego ktoś chciałbywheelsRelatedToCar nie być funkcją członka samochodu? W tym prawdziwym przykładzie samochód i koło znajdowały się w tej samej bibliotece. Funkcja globalna została zdefiniowana w pliku źródłowym w konkretnej aplikacji używającej tej biblioteki, więc argumentowano, że funkcja jest specyficzna dla aplikacji. Moja odpowiedź była taka, że ​​była to całkowicie legalna operacja na samochodzie i należała do klasy samochodów. Czy jest inna perspektywa, którą można na to spojrzeć (inna niż ta, która nie preferuje projektowania zorientowanego obiektowo)?

questionAnswers(10)

yourAnswerToTheQuestion