[BlindSpot] Log 03. Let's follow the SOLID principles : SRP
Source: Dev.to
Overview
SOLID principles are the five design principles of object‑oriented programming.
- SRP (Single Responsibility Principle) – a class (object) should have only one responsibility (function).
- DIP (Dependency Inversion Principle) – higher‑level modules should depend on abstractions (interfaces) rather than on concrete implementations of lower‑level modules.
Refactoring Example
Before
// ServerPacketHandler is both receiving and processing packets.
void ServerPacketHandler::Handle_MAKE_ROOM_REQUEST(std::shared_ptr<Session> session,
blindspot::MakeRoomRequest& pkt) {
if (session->room.lock()) {
// Already in a room
blindspot::MakeRoomResponse res;
res.set_result(blindspot::MAKE_ALREADY_IN_ROOM);
session->Send(blindspot::PacketID::ID_MAKE_ROOM_RESPONSE, res);
return;
}
std::string title = pkt.room_name();
int32_t maxPlayers = pkt.max_players();
std::string password = pkt.password();
// ...omission
}
After
// Delegates the work to a dedicated service, respecting SRP.
void ServerPacketHandler::Handle_JOIN_ROOM_REQUEST(std::shared_ptr<Session> session,
blindspot::JoinRoomRequest& pkt) {
RoomService::JoinRoom(session, pkt);
}
Session and Player Separation
Player Model
// Models/Player.h
class Player {
public:
int32_t id;
std::string name;
std::mutex _nameLock;
std::weak_ptr<Room> room;
void SetName(const std::string& playerName) {
std::lock_guard<std::mutex> lock(_nameLock);
name = playerName;
}
std::string GetName() {
std::lock_guard<std::mutex> lock(_nameLock);
return name;
}
};
Session Model
// Network/Session.h
class Session : public std::enable_shared_from_this<Session> {
public:
explicit Session(tcp::socket socket) : socket_(std::move(socket)) {}
std::shared_ptr<Player> player_;
std::string _sessionKey;
// ... other communication‑related members
private:
tcp::socket socket_;
};
Manager Classes Before Refactor
PlayerManager
// PlayerManager.h
class PlayerManager {
static std::atomic<int32_t> playerIdGenerator_;
static std::mutex name_mutex_;
static std::map<int32_t, std::string> playerIdToName_;
public:
static PlayerManager& Instance();
int32_t GeneratePlayerId();
void RegisterPlayerName(int32_t playerId, const std::string& name);
void EditPlayerName(int32_t playerId, const std::string& newName);
std::string GetPlayerNameById(int32_t playerId);
};
AuthManager
// AuthManager.h
class AuthManager {
static std::mutex token_mutex_;
static std::map<std::string, int32_t> sessionKeyToPlayerId_;
static std::mutex name_mutex_;
static std::map<int32_t, std::string> playerIdToName_;
public:
static AuthManager& Instance();
static int32_t GetPlayerIdBySessionKey(const std::string& token);
static std::string GenerateSessionKey();
static void RemoveSession(const std::string& token);
static void RegisterSession(const std::string& token, int32_t playerId);
};
SessionManager
// SessionManager.h
class SessionManager {
std::mutex sessions_mutex_;
std::set<std::shared_ptr<Session>> sessions_;
public:
static SessionManager& Instance();
void Add(std::shared_ptr<Session> session);
void Remove(std::shared_ptr<Session> session);
void Broadcast(uint16_t id, google::protobuf::Message& msg);
};
Future Work
The current refactor aligns the code with SRP. The next step will be to apply the Dependency Inversion Principle (DIP), extracting abstractions for lower‑level modules (e.g., networking, storage) so that higher‑level services depend on interfaces rather than concrete implementations.