• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Lepsza wersja kodu.

Object Storage Arubacloud
0 głosów
181 wizyt
pytanie zadane 22 stycznia 2019 w C i C++ przez Huberti Gaduła (4,500 p.)
edycja 22 stycznia 2019 przez Huberti

Witam, mam taką klasę z gry kółko i krzyżyk:

#pragma once

#include "Cell.h"
#include <ostream>
#include <array>

class GameBoard {
public:
	Cell& operator[](std::size_t index) { return gameBoard[index]; }

	const Cell& operator[](std::size_t index) const { return gameBoard[index]; }

	std::size_t getRowCount() const { return rowCount; }

	std::size_t getColumnCount() const { return columnCount; }

	std::size_t getSize() const { return gameBoard.size(); }

	friend std::ostream& operator<<(std::ostream& os, const GameBoard& gameBoard);

private:
	static const std::size_t rowCount = 3, columnCount = 3;

	std::array<Cell, rowCount * columnCount> gameBoard;
};

Brakuje jeszcze poniższych metod:

  • isAnyWinner()
  • isCircleWinner()
  • isSharpWinner()
  • isTie()

Teraz jest kilka możliwości:

Wersja 1: Umieścić powyższe metody w klasie GameBoard.

Wersja 2: Utworzyć osobną klasę np. GameBoardChecker.

Pytanie: Która z powyższych wersji będzie lepsza i dlaczego? Czy istnieje jakiś wzorzec projektowy wykorzystywany w takiej sytuacji?

Tutaj obie wersje w kodzie:

Wersja 1:

#pragma once

#include "Cell.h"
#include <ostream>
#include <array>

class GameBoard {
public:
	Cell& operator[](std::size_t index) { return gameBoard[index]; }

	const Cell& operator[](std::size_t index) const { return gameBoard[index]; }

	std::size_t getRowCount() const { return rowCount; }

	std::size_t getColumnCount() const { return columnCount; }

	std::size_t getSize() const { return gameBoard.size(); }

	bool isAnyWinner() const;

	bool isCircleWinner() const;

	bool isSharpWinner() const;

	bool isTie() const;

	friend std::ostream& operator<<(std::ostream& os, const GameBoard& gameBoard);

private:
	static const std::size_t rowCount = 3, columnCount = 3;

	std::array<Cell, rowCount * columnCount> gameBoard;
};

Wersja 2:

#pragma once

#include "GameBoard.h"

class GameBoardChecker {
public:
	bool isAnyWinner(const GameBoard& gameBoard) const;

	bool isCircleWinner(const GameBoard& gameBoard) const;

	bool isSharpWinner(const GameBoard& gameBoard) const;

	bool isTie(const GameBoard& gameBoard) const;
};

 

1
komentarz 23 stycznia 2019 przez mokrowski Mędrzec (155,460 p.)
Innym pytaniem jest z jakiego powodu masz tyle getterów? IMHO w tym kodzie świadczą o złamaniu zasady SRP (ang. Single Responsibility Principle). GameBoard oprócz samą nazwą wskazującej odpowiedzialności obsługi i trzymania planszy gry, zajmuje oddawaniem informacji o wewnętrznej implementacji. Czy naprawdę powinna prezentować takie szczegóły wszystkim zainteresowanym?

Jak w 30 masz wartości statyczne i stałe, nie ma sensu robienia do nich akcesorów czyli getterów.
komentarz 24 stycznia 2019 przez Huberti Gaduła (4,500 p.)
edycja 24 stycznia 2019 przez Huberti

To w jaki sposób klasa GameBoardChecker będzie mogła sprawdzić, czy wystąpił zwycięzca na planszy, bez dostępu do jej pól?

Idąc dalej, w jaki sposób będzie można sprawdzić, czy jakieś pole nie jest już zajęte przez innego gracza, bez odsłaniania implementacji GameBoard?

Jak w 30 masz wartości statyczne i stałe, nie ma sensu robienia do nich akcesorów czyli getterów.

Możliwe, jednak pozwala to na łatwe rozszerzenie w przyszłości, kiedy rozmiar planszy do gry będzie zależeć od użytkownika, a nie będzie stały. 

1
komentarz 24 stycznia 2019 przez mokrowski Mędrzec (155,460 p.)
edycja 24 stycznia 2019 przez mokrowski
#include <iostream>
#include <vector>

class GameRules {
public:
	GameRules() {}
	void checkBoard(const std::vector<std::vector<int>>& fields) {
		// TODO: Check rules... 
		std::cout << "Check rules...\n";
	}
};

class Board {
public:
	const std::size_t width;

	// If fields in vector<int>(width x width)
	explicit Board(std::size_t width_)
		: width{width_}, fields(width_, std::vector<int>(width_)) {}

	// Non copyable...
	Board(const Board&) = delete;
	Board& operator=(const Board&) = delete;

	void checkRules(GameRules& rules) const {
		rules.checkBoard(fields);
	}
private:
	std::vector<std::vector<int>> fields;

};

int main() {
	// (2)
	// Możliwe, jednak pozwala to na łatwe rozszerzenie w przyszłości,
	// kiedy rozmiar planszy do gry będzie zależeć od użytkownika, a nie będzie stały. 
	Board board{3};
	std::cout << "Board size: " << board.width << '\n';
	// (1)
	// To w jaki sposób klasa GameBoardChecker będzie mogła sprawdzić,
	// czy wystąpił zwycięzca na planszy, bez dostępu do jej pól?
	GameRules rules;
	board.checkRules(rules);
}

 

komentarz 24 stycznia 2019 przez Huberti Gaduła (4,500 p.)
edycja 24 stycznia 2019 przez Huberti
Dziękuję :)

1 odpowiedź

+3 głosów
odpowiedź 22 stycznia 2019 przez criss Mędrzec (172,590 p.)
wybrane 23 stycznia 2019 przez Huberti
 
Najlepsza
Obie są sensowne, ale osobna klasa do sprawdzania pozwala ci np. stworzyć klase-interfejs (abstrakcja) takiego "sprawdzacza" i na zasadach polimorfizmu móc sprawdzać wyniki wg różnych zasad (jedna klasa sprawdzacza na jeden zestaw zasad). Generalnie pozwala na więcej. Zasada "im więcej klas tym lepiej" prawie zawsze działa.

tldr: z tych dwóch ja bym brał 2
komentarz 24 stycznia 2019 przez Huberti Gaduła (4,500 p.)

Dzięki za odpowiedź, mam jeszcze jedno pytanie. Przypuśćmy taką klasę:

#pragma once

class GameBoard;
class Player;

class GameBoardChecker {
public:
	virtual bool isAnyWinner(const GameBoard& gameBoard) const = 0;

	virtual bool isWinner(const GameBoard& gameBoard, const Player& player) const = 0;

	virtual bool isTie(const GameBoard& gameBoard) const = 0;
};

Teraz będzie potrzebna klasa taka jak ta:

#pragma once

#include "GameBoardChecker.h"

class Foo : public GameBoardChecker {
public:
	bool isAnyWinner(const GameBoard& gameBoard) const;

	bool isWinner(const GameBoard& gameBoard, const Player& player) const;

	bool isTie(const GameBoard& gameBoard) const;
};

Pytanie: Jak ją nazwać? Implementuje ona sprawdzanie według algorytmu: gracz jest zwycięzcą, gdy zajmuje wszystkie komórki w rzędzie lub kolumnie lub po przekątnej planszy do gry. Wystąpił remis, gdy wszystkie pola są już zajęte.

1
komentarz 24 stycznia 2019 przez criss Mędrzec (172,590 p.)
TicTacToeChecker? :D
komentarz 24 stycznia 2019 przez Huberti Gaduła (4,500 p.)
Dzięki :D

Podobne pytania

0 głosów
2 odpowiedzi 191 wizyt
pytanie zadane 12 stycznia 2019 w C i C++ przez Huberti Gaduła (4,500 p.)
0 głosów
1 odpowiedź 116 wizyt
pytanie zadane 11 kwietnia 2019 w C i C++ przez Huberti Gaduła (4,500 p.)
0 głosów
3 odpowiedzi 528 wizyt

92,555 zapytań

141,403 odpowiedzi

319,557 komentarzy

61,940 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...