前回発生した問題に対して、オブジェクト指向で設計、実装されていたら、どうだったかを検証します。ここで考えるのは、前々回の段階です。
その前に、「オブジェクト指向」って、難しいのでしょうか?ということを考えます。「難しいのでしょうか?」というのは、正しくありません。「何が、難しいのでしょうか?」を考えます。
普通、人は、何かしなければならないとき、「どうする」か、考えます。あるいは、達成した状態になるために、どのようなことが必要か考えます。このとき考えることは、「行為」のみではないでしょうか。
とっても簡単な例では、「彼に、醤油をとってもらいたい」とします。私は、「醤油をとって」と頼みます。このように、「彼に頼む」という行為をすればよい、と考えるわけです。
ところが、オブジェクト指向では、「誰にさせるか」ということが重要になります。「私」にも「彼」にも、「ものを運ぶ」という行為はできます。したがって、「私」が「ものを運ぶ」のか、「彼」が「ものを運ぶ」のか、考えなければなりません。そして、「彼」に仕事をするために、「私」ができることは何か?と、考えます。そうすると、「彼に頼む」とだけ考えればよかった手続き指向に対して、オブジェクト指向では「誰が、彼に頼むのか」を考えなければならない、、、と、言えなくもありません。確かに面倒ではありそうです。つまり、手続き指向では「私が」という暗黙の了解があったわけです。ところが、オブジェクト指向では「私」はプログラムから切り離され、独立してしまうのです。そして「私」は、プログラム内の様々な「誰か」に対して、「他の人に頼む」という行為をさせることになります。そして、頼むためには、その「誰か」が、対象のことを「できる」かどうか、確認しておかなければならない、ということでもあります。
.NET Framework には、System.Windows.Forms.Form というクラスがあります。この「クラス」が「誰か」です。Form クラスには、Show というメソッドがあります。つまり、Form クラスは現れる(Show)ことができます。しかし、System.String クラスには Show メソッドがありません。したがって、String クラスは現れることができません。
余談:この原則を壊すのが、拡張メソッドですね。後付のような感じで「~することができる」を増やすことができます。
閑話休題。このように、オブジェクト指向では、「誰」ということ、その誰かが「何ができるか」について、注意を払わなければなりません。また、「誰か」つまりクラスを設計する場合は、そのクラスに「何をさせるべきか」について、注意を払わなければなりません。
オブジェクト指向が難しいのは、この、いわば「キャスティング」ではないでしょうか。ひとつのアプリケーションという演劇に、どのような俳優すなわちクラスを起用するのか。俳優によって得意とすることが異なります。同じように、クラスによってできること、得意なことが異なります。場合によっては、というかほとんどの場合、新しい俳優を捜さなければならないでしょう。すなわち、クラスの設計です。
では、キャスティング...というか、すでに潜んでいる俳優たちを見つけ出します。
まず、「停止させられ」たり、「起動させられ」たりする、「サービス」です。他者から「~させられる」ということは、自立して「~できる」ということです。
それから、サービス マネージャも、キャストとしてあげておきましょう。
こんなモンですかね。では、彼らは、どんなことができるのか、考えます。
サービス マネージャは、「アクセスする(される)」ことと、「アクセスを切り離す(切り離される)」ことができます。.NET Framework ではこれは「アンマネージド リソース」として扱うのですが、今回は Unmanaged C++ を考えているので、とりあえず、無視。他に、「ハンドル」というプロパティが必要ですね。これは、アクセスすることで得られるものなので、「読み取り専用」とします。
サービスは、「停止する(停止させられる)」ことと「起動する(起動させられる)」ことができます。サービスにもハンドルが必要ですが、これは、今回の用途には必要がないので、private にしておきます(アクセッサーを用意しない)。ハンドルがあるということは、「アクセスする」と「切り離す」もありますね。そして、起動や停止が完了するのを、「どれくらい待つか」というのを、指定できるようにしておきましょう。ただし、デフォルトを30秒として、また、30秒未満には設定できないようにします。
// サービス マネージャ ヘッダ
#pragma once
class ServiceManager
{
private:
SC_HANDLE handle;
public:
ServiceManager(void);
virtual ~ServiceManager(void);
DWORD Open(DWORD desiredAccess);
void Close(void);
const SC_HANDLE get_Handle(void);
};
// サービス マネージャ
#include "ServiceManager.h"
ServiceManager::ServiceManager(void)
{
handle = NULL;
}
ServiceManager::~ServiceManager(void)
{
if (handle != NULL) {
Close();
}
}
DWORD ServiceManager::Open(DWORD desiredAccess)
{
if (handle == NULL) {
handle = OpenSCManager(NULL, NULL, desiredAccess);
if (handle == NULL) {
DWORD ret = GetLastError();
SetLastError(ret);
return ret;
}
}
SetLastError(ERROR_SUCCESS);
return ERROR_SUCCESS;
}
void ServiceManager::Close(void)
{
CloseServiceHandle(handle);
handle = NULL;
}
const SC_HANDLE ServiceManager::get_Handle(void)
{
return handle;
}
// サービス ヘッダ
#pragma once
class LocalService
{
private:
SC_HANDLE handle;
static unsigned int FixTimeout(unsigned int timeout) {
// 30以上60未満に整形
return (timeout < 30 ? 30 : (timeout > 60 ? 60 : timeout));
};
DWORD WaitForState(DWORD status, unsigned int timeout);
public:
LocalService(void);
virtual ~LocalService(void);
DWORD Open(SC_HANDLE manager, LPCTSTR serviceName, DWORD desiredAccess);
void Close(void);
DWORD Start(unsigned int timeout = 30);
DWORD Stop(unsigned int timeout = 30);
};
// サービス
#include "LocalService.h"
LocalService::LocalService(void)
{
handle = NULL;
}
LocalService::~LocalService(void)
{
if (handle != NULL) {
Close();
}
}
DWORD LocalService::Open(SC_HANDLE manager, LPCTSTR serviceName, DWORD desiredAccess)
{
if (serviceName == NULL || manager == NULL || serviceName[0] == 0) {
SetLastError(ERROR_BAD_ARGUMENTS);
return ERROR_BAD_ARGUMENTS;
}
if (handle != NULL) {
Close();
}
handle = OpenService(manager, serviceName, desiredAccess);
if (handle == NULL) {
DWORD result = GetLastError();
SetLastError(result);
return result;
}
SetLastError(ERROR_SUCCESS);
return ERROR_SUCCESS;
}
void LocalService::Close(void)
{
(void) CloseServiceHandle(handle);
handle = NULL;
}
DWORD LocalService::Start(unsigned int timeout)
{
if (handle == NULL) {
return ERROR_NOT_READY;
}
timeout = FixTimeout(timeout);
BOOL ret = StartService(handle, 0, NULL);
DWORD result = GetLastError();
if (ret == TRUE || result == ERROR_SERVICE_ALREADY_RUNNING) {
result = WaitForState(SERVICE_RUNNING, timeout);
}
SetLastError(result);
return result;
}
DWORD LocalService::Stop(unsigned int timeout)
{
if (handle == NULL) {
return ERROR_NOT_READY;
}
timeout = FixTimeout(timeout);
SERVICE_STATUS status;
BOOL ret = ControlService(handle, SERVICE_CONTROL_STOP, &status);
DWORD result = GetLastError();
if (ret == TRUE || result == ERROR_SERVICE_NOT_ACTIVE) {
result = WaitForState(SERVICE_STOPPED, timeout);
}
SetLastError(result);
return result;
}
DWORD LocalService::WaitForState(DWORD waitFor, unsigned int timeout)
{
DWORD result;
SERVICE_STATUS status;
int count = 0;
while (TRUE) {
if (QueryServiceStatus(handle, &status)) {
if (status.dwCurrentState == waitFor) {
result = ERROR_SUCCESS;
break;
}
if (++count > timeout) {
result = ERROR_SERVICE_REQUEST_TIMEOUT;
break;
}
} else {
result = GetLastError();
break;
}
Sleep(1000);
}
return result;
}
投稿日時 : 2008年9月17日 22:43