Wzorce projektowe - zarządzanie wykonywaniem zadań.

0

Napisałem taki kod:

#include "stdafx.h"
#include <iostream>
#include <memory>
#include <string>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cassert>

class IOutput
{
public:
	virtual ~IOutput() {}

	virtual void write(const char *str) = 0;
};

class Display : public IOutput
{
public:
	void write(const char *str) override {std::cout << str << std::endl; };
};

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

	virtual void read() = 0;
	virtual int select() = 0;
};

class Serial : public ISerial 
{
public:
	Serial() { srand(static_cast<unsigned int>(time(NULL))); }

	void read() override { std::cout << "Serial reading data" << std::endl; }
	int select() override { return rand() % 2; }
};

class Manager : public std::enable_shared_from_this<Manager> {
private:
	std::unique_ptr<ISerial> iserial_;
	std::unique_ptr<IOutput> idisplay_;

	void doStaff1(void)
	{
		idisplay_->write("Do some staff 1.");
	}
	void doStaff2(void) 
	{
		idisplay_->write("Do some staff 2.");
	}
	void doStaff3(void)
	{
		idisplay_->write("Do some staff 3.");
	}
	void doStaff4(void)
	{
		idisplay_->write("Do some staff 1.");
	}

public:
	Manager(std::unique_ptr<ISerial> iserial, std::unique_ptr<IOutput> idisplay) : iserial_(std::move(iserial)), idisplay_(std::move(idisplay)) {}
	
	void doJob();
	void doComplex1()
	{
		doStaff3();
		doStaff4();
	}
	void doComplex2()
	{
		doStaff4();
		doStaff3();
	}
};

class IWorker 
{
private:
	
protected:
	std::shared_ptr<Manager> manager_;

public:
	IWorker(std::shared_ptr<Manager>manager) : manager_(manager) {}
	virtual ~IWorker() {}

	virtual void doIt() = 0;
	virtual const char* getName() = 0;

	static std::unique_ptr<IWorker> create(unsigned int type, std::shared_ptr<Manager>manager); 
};

class Subworker1 : public IWorker 
{
public:
	Subworker1(std::shared_ptr<Manager> manager) : IWorker(manager) {}

	void doIt() override { manager_->doComplex1(); }
	const char* getName() override { return "Subworker 1"; }
};

class Subworker2 : public IWorker
{
public:
	Subworker2(std::shared_ptr<Manager> manager) : IWorker(manager) {}

	void doIt() override { manager_->doComplex2();}
	const char* getName() override { return "Subworker 2"; }
};

std::unique_ptr<IWorker> IWorker::create(unsigned int type, std::shared_ptr<Manager>manager) 
{
	std::unique_ptr<IWorker> result;

	switch(type) 
	{
	case 0:
		result = std::unique_ptr<IWorker>(new Subworker1(manager));
		break;
	case 1:
		result = std::unique_ptr<IWorker>(new Subworker2(manager));
		break;
	}

	return result;
}


void Manager::doJob() 
{
	doStaff1();
	doStaff2();

	// Wybieram podwykonawcę (który będzie używał narzędzi menagara). Przed wykonaniem doStaff1, doStaff2 nie wiem kogo będę potrzebował.
	std::unique_ptr<IWorker> worker = IWorker::create(iserial_->select(), shared_from_this());

	worker->doIt();
	
	idisplay_->write(worker->getName());
}


int main(int argc, char * argv[])
{
	std::shared_ptr<Manager> man = std::shared_ptr<Manager>(new Manager(std::unique_ptr<ISerial>(new Serial), std::unique_ptr<IOutput>(new Display)));

	man->doJob();

	return 0;
}


A teraz napiszę co chciałem zrobić:

  • Obiekt klasy Manager ma zarządzać interfejsami;
  • W momencie utworzenia obiektu Manager nie wiemy jaki podwykonawca będzie nam potrzebny, ale potrzebny będzie max 1;
  • Liczba dostępnych rodzajów podwykonawców może ulec zmianie;
  • Podwykonawcy wykonają prace metodami menagera;
  • Chcę to osiągnąć przy możliwie najmniejszej liczbie klas;

Czyli podsumowując. Tworzę obiekt menagera, który po odebraniu informacji decyduje kogo zatrudnić, bo nie wie jak wykonywać pracę (ale dysponuje narzędziami).

0

To o czym mówisz kojarzy mi się z fabryką abstrakcyjną. Poczytaj o tym.

0

Wzorzec znam, chociaż tutaj poszedłem w nieco prostszą wersję metody fabrykującej. Generalnie, to co wstawiłem wyżej sprawuje się całkiem dobrze. Mogę modyfikować istniejących, lub modyfikować nowych worker'ów, a klasie Manager jest to obojętne :)

Jedyne co mnie tu zastanawiało to fakt pewnego zakleszczenia, bo Manager tworzy Workera, który odwołuje się do Managera (który go stworzył) :D

0

Subworkerzy nie powinni dostawać ptr na managera tylko jakiś interfejs. Teraz subworker1 może bez problemu wywołać sobie metodę Manager::doComplex2

0

No, ale między innymi o to chodziło, aby mógł tak zrobić. Co tutaj zmieni interfejs do managera? Jedynie, to, że w przyszłości będzie można podmienić klasę Manager na inną, ale zakładam tutaj stałość klasy Manager.

0

A jakie ma byc zastosowanie tego manadzera? Do czego i dlaczego ma byc uzyty?

0
Sparrow-hawk napisał(a):

Jedyne co mnie tu zastanawiało to fakt pewnego zakleszczenia, bo Manager tworzy Workera, który odwołuje się do Managera (który go stworzył) :D

Więc nie masz pojęcia czemu tak to zakodowałeś - ergo nie wiesz co robisz.

0

@fasadin Manager ma izolować workerów od reszty systemu. Ma dostarczać im jeden interfejs. Na tą chwilę zakładam sporą zmienność workerów, i praktycznie stałość obiektu Manager.

1 użytkowników online, w tym zalogowanych: 0, gości: 1