Share
Embed
コードに語らせよう 自己ドキュメント化が内包する楽しさについて / Let the Code Speak: The Joy of Self-Documenting Code
Transcript
94 slides
-
function check($c, $t, $s) { if ($t < 9 ||
$t > 18) return false; if ($s == 1 && count($c->getP()) >= 30) return false; if ($s == 2 && count($c->getP()) >= 20) return false; if ($c->age < 3 && $s != 1) return false; return true; } -
function check($c, $t, $s) { if ($t < 9 ||
$t > 18) return false; if ($s == 1 && count($c->getP()) >= 30) return false; if ($s == 2 && count($c->getP()) >= 20) return false; if ($c->age < 3 && $s != 1) return false; return true; } なにやってんだこれー? -
class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,
int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : -
class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,
int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : 子どもを受け入れられるか -
class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,
int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : クラス -
class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,
int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : 何時からを希望? 何歳? -
class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,
int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : 時間外じゃないかの確認 -
class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,
int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : クラスのキャパを確認 -
class EnrollmentService { public function canAcceptNewChild( Classroom $classroom, int $desiredStartHour,
int $childAge): bool { if ($this->isOutsideOperatingHours($desiredStartHour)) { return false; } if ($this->isClassroomAtCapacity($classroom)) { return false; } if ($this->isToddlerRequiringSpecializedCare($childAge, $classroom)) { return false; } return true; } : 専門的なケアが必要? -
private function isOutsideOperatingHours(int $hour): bool { return $hour < 9
|| $hour > 18; } private function isClassroomAtCapacity(Classroom $classroom): bool { return count($classroom->getEnrolledChildren()) >= $classroom->getMaxCapacity(); } private function isToddlerRequiringSpecializedCare(int $childAge, Classroom $classroom) : bool { return $childAge < 3 && !$classroom->isToddlerSpecialized(); } -
private function isOutsideOperatingHours(int $hour): bool { return $hour < 9
|| $hour > 18; } private function isClassroomAtCapacity(Classroom $classroom): bool { return count($classroom->getEnrolledChildren()) >= $classroom->getMaxCapacity(); } private function isToddlerRequiringSpecializedCare(int $childAge, Classroom $classroom) : bool { return $childAge < 3 && !$classroom->isToddlerSpecialized(); } 9時〜18時 -
private function isOutsideOperatingHours(int $hour): bool { return $hour < 9
|| $hour > 18; } private function isClassroomAtCapacity(Classroom $classroom): bool { return count($classroom->getEnrolledChildren()) >= $classroom->getMaxCapacity(); } private function isToddlerRequiringSpecializedCare(int $childAge, Classroom $classroom) : bool { return $childAge < 3 && !$classroom->isToddlerSpecialized(); } 教室キャパと人数確認 -
private function isOutsideOperatingHours(int $hour): bool { return $hour < 9
|| $hour > 18; } private function isClassroomAtCapacity(Classroom $classroom): bool { return count($classroom->getEnrolledChildren()) >= $classroom->getMaxCapacity(); } private function isToddlerRequiringSpecializedCare(int $childAge, Classroom $classroom) : bool { return $childAge < 3 && !$classroom->isToddlerSpecialized(); } 3歳未満で幼児向けに特化されてない -
class EnrollmentPolicy { public function canAccept(Child $child, Classroom $classroom, int
$desiredStartHour) : EnrollmentResult { if ($desiredStartHour < 9 || $desiredStartHour > 18) { return EnrollmentResult::rejected('outside_operating_hours'); } if (!$classroom->hasCapacityFor($child)) { return EnrollmentResult::rejected('classroom_at_capacity'); } if ($child->getAge() < 3 && !$classroom->isToddlerSpecialized()) { return EnrollmentResult::rejected('classroom_not_suitable_for_age'); } return EnrollmentResult::accepted(); } } -
class EnrollmentPolicy { public function canAccept(Child $child, Classroom $classroom, int
$desiredStartHour) : EnrollmentResult { if ($desiredStartHour < 9 || $desiredStartHour > 18) { return EnrollmentResult::rejected('outside_operating_hours'); } if (!$classroom->hasCapacityFor($child)) { return EnrollmentResult::rejected('classroom_at_capacity'); } if ($child->getAge() < 3 && !$classroom->isToddlerSpecialized()) { return EnrollmentResult::rejected('classroom_not_suitable_for_age'); } return EnrollmentResult::accepted(); } } Serviceとは別軸の処理に切り出し -
class EnrollmentPolicy { public function canAccept(Child $child, Classroom $classroom, int
$desiredStartHour) : EnrollmentResult { if ($desiredStartHour < 9 || $desiredStartHour > 18) { return EnrollmentResult::rejected('outside_operating_hours'); } if (!$classroom->hasCapacityFor($child)) { return EnrollmentResult::rejected('classroom_at_capacity'); } if ($child->getAge() < 3 && !$classroom->isToddlerSpecialized()) { return EnrollmentResult::rejected('classroom_not_suitable_for_age'); } return EnrollmentResult::accepted(); } } true / false から脱却 -
class EnrollmentResult { private bool $accepted; private ?string $rejectionReason; :
public static function accepted(): self { return new self(true); } public static function rejected(string $reason): self { return new self(false, $reason); } } -
class Age { private int $value; public function __construct(int $value)
{ $this->value = $value; } public function isUnderThree(): bool { return $this->value < 3; } } class DesiredStartTime { private int $hour; public function isWithinOperatingHours(): bool { return $this->hour >= 9 && $this->hour <= 18; } } 一長一短に思う -
function recordAttendance($child_id, $action, $timestamp) { $db = new PDO(‘sqlite:attendance.db’); $today
= date(‘Y-m-d’, $timestamp); // 今日の記録を取得 $stmt = $db->prepare(“SELECT * FROM attendance WHERE child_id = ? AND date = ?”); $stmt->execute([$child_id, $today]); $record = $stmt->fetch(); if ($action == ‘checkin’) { if ($record && $record[‘checkin_time’]) { return [‘success’ => false, ‘message’ => ‘既に登園済みです’]; } if ($record) { $stmt = $db->prepare(“UPDATE attendance SET checkin_time = ? WHERE id = ?”); $stmt->execute([date(‘H:i:s’, $timestamp), $record[‘id’]]); } else { $stmt = $db->prepare(“INSERT INTO attendance (child_id, date, checkin_time) VALUES (?, ?, ?)”) $stmt->execute([$child_id, $today, date(‘H:i:s’, $timestamp)]); } return [‘success’ => true, ‘message’ => ‘登園を記録しました’]; } : -
function recordAttendance($child_id, $action, $timestamp) { $db = new PDO(‘sqlite:attendance.db’); $today
= date(‘Y-m-d’, $timestamp); // 今日の記録を取得 $stmt = $db->prepare(“SELECT * FROM attendance WHERE child_id = ? AND date = ?”); $stmt->execute([$child_id, $today]); $record = $stmt->fetch(); if ($action == ‘checkin’) { if ($record && $record[‘checkin_time’]) { return [‘success’ => false, ‘message’ => ‘既に登園済みです’]; } if ($record) { $stmt = $db->prepare(“UPDATE attendance SET checkin_time = ? WHERE id = ?”); $stmt->execute([date(‘H:i:s’, $timestamp), $record[‘id’]]); } else { $stmt = $db->prepare(“INSERT INTO attendance (child_id, date, checkin_time) VALUES (?, ?, ?)”) $stmt->execute([$child_id, $today, date(‘H:i:s’, $timestamp)]); } return [‘success’ => true, ‘message’ => ‘登園を記録しました’]; } : ちゃんと読めば分かる →ちゃんと読まないとわからん -
function recordAttendance($child_id, $action, $timestamp) { $db = new PDO(‘sqlite:attendance.db’); $today
= date(‘Y-m-d’, $timestamp); // 今日の記録を取得 $stmt = $db->prepare(“SELECT * FROM attendance WHERE child_id = ? AND date = ?”); $stmt->execute([$child_id, $today]); $record = $stmt->fetch(); if ($action == ‘checkin’) { if ($record && $record[‘checkin_time’]) { return [‘success’ => false, ‘message’ => ‘既に登園済みです’]; } if ($record) { $stmt = $db->prepare(“UPDATE attendance SET checkin_time = ? WHERE id = ?”); $stmt->execute([date(‘H:i:s’, $timestamp), $record[‘id’]]); } else { $stmt = $db->prepare(“INSERT INTO attendance (child_id, date, checkin_time) VALUES (?, ?, ?)”) $stmt->execute([$child_id, $today, date(‘H:i:s’, $timestamp)]); } return [‘success’ => true, ‘message’ => ‘登園を記録しました’]; } : 配列かぁ -
public function recordAttendance(int $childId, string $action, DateTime $timestamp) : AttendanceResult
{ $today = $timestamp->format('Y-m-d'); switch ($action) { case 'checkin': return $this->processCheckIn($childId, $today, $timestamp); case 'checkout': return $this->processCheckOut($childId, $today, $timestamp); default: return new AttendanceResult(false, '無効な操作です'); } } -
public function recordAttendance(int $childId, string $action, DateTime $timestamp) : AttendanceResult
{ $today = $timestamp->format('Y-m-d'); switch ($action) { case 'checkin': return $this->processCheckIn($childId, $today, $timestamp); case 'checkout': return $this->processCheckOut($childId, $today, $timestamp); default: return new AttendanceResult(false, '無効な操作です'); } } 読む気になる!! -
public function recordAttendance(int $childId, string $action, DateTime $timestamp) : AttendanceResult
{ $today = $timestamp->format('Y-m-d'); switch ($action) { case 'checkin': return $this->processCheckIn($childId, $today, $timestamp); case 'checkout': return $this->processCheckOut($childId, $today, $timestamp); default: return new AttendanceResult(false, '無効な操作です'); } } 登園 -
public function recordAttendance(int $childId, string $action, DateTime $timestamp) : AttendanceResult
{ $today = $timestamp->format('Y-m-d'); switch ($action) { case 'checkin': return $this->processCheckIn($childId, $today, $timestamp); case 'checkout': return $this->processCheckOut($childId, $today, $timestamp); default: return new AttendanceResult(false, '無効な操作です'); } } 降園 -
private function processCheckIn(int $childId, string $date, DateTime $timestamp): AttendanceResult {
$record = $this->dao->findTodaysRecord($childId, $date); if ($record && $record->checkinTime) { return new AttendanceResult(false, '既に登園済みです'); } if (!$record) { $record = new AttendanceRecord(); $record->childId = $childId; $record->date = $date; } $record->checkinTime = $timestamp->format('H:i:s'); $this->dao->save($record); return new AttendanceResult(true, '登園を記録しました'); } -
private function processCheckIn(int $childId, string $date, DateTime $timestamp): AttendanceResult {
$record = $this->dao->findTodaysRecord($childId, $date); if ($record && $record->checkinTime) { return new AttendanceResult(false, '既に登園済みです'); } if (!$record) { $record = new AttendanceRecord(); $record->childId = $childId; $record->date = $date; } $record->checkinTime = $timestamp->format('H:i:s'); $this->dao->save($record); return new AttendanceResult(true, '登園を記録しました'); } 意図を語ってくれてる (SQLの意図を理解しなくていい) -
public function recordAttendance(ChildId $childId, AttendanceAction $action, DateTime $timestamp): AttendanceResult {
$todaysRecord = $this->repository->findTodaysRecord($childId); try { if ($action->isCheckIn()) { $record = $todaysRecord ?: new AttendanceRecord($childId, new Date($timestamp)); $record->recordCheckIn($timestamp); $this->repository->save($record); return AttendanceResult::checkInSuccess(); } if ($action->isCheckOut()) { if (!$todaysRecord) { return AttendanceResult::notCheckedIn(); } $todaysRecord->recordCheckOut($timestamp); $this->repository->save($todaysRecord); return AttendanceResult::checkOutSuccess(); } : -
public function recordAttendance(ChildId $childId, AttendanceAction $action, DateTime $timestamp): AttendanceResult {
$todaysRecord = $this->repository->findTodaysRecord($childId); try { if ($action->isCheckIn()) { $record = $todaysRecord ?: new AttendanceRecord($childId, new Date($timestamp)); $record->recordCheckIn($timestamp); $this->repository->save($record); return AttendanceResult::checkInSuccess(); } if ($action->isCheckOut()) { if (!$todaysRecord) { return AttendanceResult::notCheckedIn(); } $todaysRecord->recordCheckOut($timestamp); $this->repository->save($todaysRecord); return AttendanceResult::checkOutSuccess(); } : データを主語でなく、モデルを主語に -
public function recordAttendance(ChildId $childId, AttendanceAction $action, DateTime $timestamp): AttendanceResult {
$todaysRecord = $this->repository->findTodaysRecord($childId); try { if ($action->isCheckIn()) { $record = $todaysRecord ?: new AttendanceRecord($childId, new Date($timestamp)); $record->recordCheckIn($timestamp); $this->repository->save($record); return AttendanceResult::checkInSuccess(); } if ($action->isCheckOut()) { if (!$todaysRecord) { return AttendanceResult::notCheckedIn(); } $todaysRecord->recordCheckOut($timestamp); $this->repository->save($todaysRecord); return AttendanceResult::checkOutSuccess(); } : データを確認するのでなく、オブジェクトに移譲 -
function validatePickup($person_id, $child_id, $current_time) { $db = new PDO('sqlite:nursery.db'); //
子供の情報を取得 $stmt = $db->prepare("SELECT * FROM children WHERE id = ? AND status = 'present'"); $stmt->execute([$child_id]); $child = $stmt->fetch(); if (!$child) { return ['success' => false, 'message' => '子供が見つからないか、まだ登園していません']; } // 迎えに来た人の情報を取得 $stmt = $db->prepare("SELECT * FROM persons WHERE id = ?"); $stmt->execute([$person_id]); $person = $stmt->fetch(); if (!$person) { return ['success' => false, 'message' => '迎えに来た方の情報が見つかりません']; } // 親または保護者の場合は常に許可 if ($person['type'] == 'parent' || $person['type'] == 'guardian') { return ['success' => true, 'message' => 'お迎え可能です']; } // 事前に承認された人の場合 $stmt = $db->prepare("SELECT * FROM authorized_persons WHERE child_id = ? AND person_id = ? AND is_active = 1"); $stmt->execute([$child_id, $person_id]); $authorized = $stmt->fetch(); if ($authorized) { return ['success' => true, 'message' => 'お迎え可能です']; } // 緊急連絡先の場合(15分前までに連絡が必要) if ($person['type'] == 'emergency') { $stmt = $db->prepare("SELECT * FROM pickup_notifications WHERE child_id = ? AND person_id = ? AND notify_time <= ?"); $notify_deadline = $current_time - (15 * 60); // 15分前 $stmt->execute([$child_id, $person_id, $notify_deadline]); $notification = $stmt->fetch(); if ($notification) { return ['success' => true, 'message' => 'お迎え可能です']; } else { return ['success' => false, 'message' => '緊急連絡先の方は15分前までにご連絡ください']; } } return ['success' => false, 'message' => 'お迎えの権限がありません']; } -
public function validatePickup(int $personId, int $childId, DateTime $currentTime): PickupResult {
$child = $this->childDAO->find($childId); if (!$child || !$child->isPresentAtNursery()) { return new PickupResult(false, '子供が見つからないか、まだ登園していません'); } $person = $this->personDAO->find($personId); if (!$person) { return new PickupResult(false, '迎えに来た方の情報が見つかりません'); } if ($this->canPickupChild($person, $childId, $currentTime)) { return new PickupResult(true, 'お迎え可能です'); } return new PickupResult(false, 'お迎えの権限がありません'); } -
public function validatePickup(int $personId, int $childId, DateTime $currentTime): PickupResult {
$child = $this->childDAO->find($childId); if (!$child || !$child->isPresentAtNursery()) { return new PickupResult(false, '子供が見つからないか、まだ登園していません'); } $person = $this->personDAO->find($personId); if (!$person) { return new PickupResult(false, '迎えに来た方の情報が見つかりません'); } if ($this->canPickupChild($person, $childId, $currentTime)) { return new PickupResult(true, 'お迎え可能です'); } return new PickupResult(false, 'お迎えの権限がありません'); } 同じ内容でも見るべき場所がここであると語っている -
private function canPickupChild(Person $person, int $childId, DateTime $currentTime): bool {
if ($person->isParentOrGuardian()) { return true; } if ($this->authorizationDAO->isAuthorized($childId, $person->id)) { return true; } if ($person->isEmergencyContact() && $this->hasValidEmergencyNotification($childId, $person->id, $currentTime)) { return true; } return false; } -
保護者か? private function canPickupChild(Person $person, int $childId, DateTime $currentTime): bool
{ if ($person->isParentOrGuardian()) { return true; } if ($this->authorizationDAO->isAuthorized($childId, $person->id)) { return true; } if ($person->isEmergencyContact() && $this->hasValidEmergencyNotification($childId, $person->id, $currentTime)) { return true; } return false; } -
private function canPickupChild(Person $person, int $childId, DateTime $currentTime): bool {
if ($person->isParentOrGuardian()) { return true; } if ($this->authorizationDAO->isAuthorized($childId, $person->id)) { return true; } if ($person->isEmergencyContact() && $this->hasValidEmergencyNotification($childId, $person->id, $currentTime)) { return true; } return false; } 権限はあるか -
private function canPickupChild(Person $person, int $childId, DateTime $currentTime): bool {
if ($person->isParentOrGuardian()) { return true; } if ($this->authorizationDAO->isAuthorized($childId, $person->id)) { return true; } if ($person->isEmergencyContact() && $this->hasValidEmergencyNotification($childId, $person->id, $currentTime)) { return true; } return false; } 緊急連絡先であり、有効な緊急通知権限をもっているか -
class PickupAuthorizationPolicy { : public function canPickUp(Person $person, Child $child,
DateTime $requestTime): PickupAuthorization { if ($person->isParentOf($child) || $person->isGuardianOf($child)) { return PickupAuthorization::approved(); } if ($child->hasAuthorizedPerson($person)) { return PickupAuthorization::approved(); } if ($person->isEmergencyContactFor($child)) { $notificationDeadline = (clone $requestTime)->modify('-15 minutes'); if ($this->notificationRepository->hasAdvanceNotificationFor( $child->getId(), $person->getId(), $notificationDeadline)) { return PickupAuthorization::approved(); } return PickupAuthorization::denied('emergency_no_notification'); } return PickupAuthorization::denied('unauthorized_person'); } } -
class PickupAuthorizationPolicy { : public function canPickUp(Person $person, Child $child,
DateTime $requestTime): PickupAuthorization { if ($person->isParentOf($child) || $person->isGuardianOf($child)) { return PickupAuthorization::approved(); } if ($child->hasAuthorizedPerson($person)) { return PickupAuthorization::approved(); } if ($person->isEmergencyContactFor($child)) { $notificationDeadline = (clone $requestTime)->modify('-15 minutes'); if ($this->notificationRepository->hasAdvanceNotificationFor( $child->getId(), $person->getId(), $notificationDeadline)) { return PickupAuthorization::approved(); } return PickupAuthorization::denied('emergency_no_notification'); } return PickupAuthorization::denied('unauthorized_person'); } } <「お迎えできるかは私を見なさい」 -
92 仲間を探しています 子どもを取り巻く環境をテクノロジーの力でよりよいものにしていく仲間を大募集! • 子育てしやすい環境です ◦ 子育てにドメイン知識がありすぎるメンバー ◦ フレックス/フルリモート可 •
開発者体験が最高です ◦ アジャイル(XP) ◦ バーチャルオフィス(Gather) ◦ ペアプロでコードについて議論できます ◦ CTOが財布 -
94 • X ◦ @nrslib • HomePage ◦ https://nrslib.com/ •
YouTube ◦ https://www.youtube.com/@nrslib おしまい