You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何合理消除嵌套foreach循环?寻求设计模式相关指导

Hey there! Let's break down how to clean up that nested foreach and level up your code structure—you're already on the right track by moving logic into the Doctor class, so let's build on that.

First: Validate Your Current Approach

Your idea to shift the diagnosis formatting into the Doctor class is great—it aligns with the Single Responsibility Principle (one of the SOLID fundamentals). The Treatment class shouldn’t be responsible for merging doctor details with diagnoses; that’s the doctor’s job to own their own data context.

Better Optimizations to Eliminate Nested Loops

Let’s refine this further with a few practical steps:

1. Replace Inner Loops with Functional Mapping

Instead of writing an explicit foreach inside Doctor to build the diagnosis objects, use array_map to keep the code concise and declarative:

class Doctor {
    // ... existing methods

    public function getDiagnosisWithDoctor(): array {
        // Use array_map to transform each diagnosis into a structured object
        return array_map(function($diagnosis) {
            $std = new \StdClass();
            $std->docType = $this->getType();
            $std->docLastname = $this->getLastname();
            $std->docFirstname = $this->getFirstname();
            $std->diagnosis = $diagnosis;
            return $std;
        }, $this->getCurrentDiagnosis());
    }
}

Then your Treatment class can simplify to just collecting results—no nested loops at all:

class Treatment {
    public function getDiagnosis($type = 0): array {
        $diagnosisContainer = [];
        foreach($this->doctorIterator as $doc) {
            $diagnosisContainer = array_merge($diagnosisContainer, $doc->getDiagnosisWithDoctor());
        }
        // Match your original reverse order if needed
        return array_reverse($diagnosisContainer);
    }
}

2. Use a Value Object Instead of StdClass

StdClass is flexible but lacks type safety and clarity. Create a dedicated value object to represent the combined doctor-diagnosis data—this makes your code more maintainable and self-documenting:

class DiagnosisWithDoctor {
    public function __construct(
        private string $docType,
        private string $docLastname,
        private string $docFirstname,
        private mixed $diagnosis
    ) {}

    // Add getters (or use PHP 8.1+ readonly properties for brevity)
    public function getDocType(): string { return $this->docType; }
    public function getDocLastname(): string { return $this->docLastname; }
    public function getDocFirstname(): string { return $this->docFirstname; }
    public function getDiagnosis(): mixed { return $this->diagnosis; }
}

Update the Doctor method to return this object instead:

public function getDiagnosisWithDoctor(): array {
    return array_map(function($diagnosis) {
        return new DiagnosisWithDoctor(
            $this->getType(),
            $this->getLastname(),
            $this->getFirstname(),
            $diagnosis
        );
    }, $this->getCurrentDiagnosis());
}

3. Optimize Memory with Iterators (For Large Datasets)

If you’re dealing with lots of doctors/diagnoses, loading everything into an array at once can waste memory. Use an iterator to lazily fetch results as you need them:

class DiagnosisIterator implements Iterator {
    private $doctorIterator;
    private array $currentDiagnoses = [];
    private int $position = 0;

    public function __construct(Iterator $doctorIterator) {
        $this->doctorIterator = $doctorIterator;
        $this->doctorIterator->rewind();
        $this->loadCurrentDoctorDiagnoses();
    }

    private function loadCurrentDoctorDiagnoses(): void {
        if ($this->doctorIterator->valid()) {
            $this->currentDiagnoses = $this->doctorIterator->current()->getDiagnosisWithDoctor();
        } else {
            $this->currentDiagnoses = [];
        }
        $this->position = 0;
    }

    public function rewind(): void {
        $this->doctorIterator->rewind();
        $this->loadCurrentDoctorDiagnoses();
    }

    public function current(): mixed {
        return $this->currentDiagnoses[$this->position];
    }

    public function key(): string {
        return $this->doctorIterator->key() . '_' . $this->position;
    }

    public function next(): void {
        $this->position++;
        if ($this->position >= count($this->currentDiagnoses)) {
            $this->doctorIterator->next();
            $this->loadCurrentDoctorDiagnoses();
        }
    }

    public function valid(): bool {
        return !empty($this->currentDiagnoses) && $this->position < count($this->currentDiagnoses);
    }
}

Then your Treatment method can return the iterator instead of an array:

public function getDiagnosis($type = 0): Iterator {
    return new DiagnosisIterator($this->doctorIterator);
}

Callers can still loop through it like an array, but memory usage stays low.

What Design Patterns Are You Missing?

Your confusion ties back to two core concepts:

  • Single Responsibility Principle: Each class should handle one job. Treatment manages the doctor collection, Doctor manages its own data and diagnosis formatting, and DiagnosisWithDoctor holds the combined data.
  • Iterator Pattern: Encapsulates traversal logic for complex collections, so you don’t need messy nested loops in your business code.

You’re already moving in the right direction—these patterns just formalize the good instincts you already have!

内容的提问来源于stack exchange,提问作者Christian Felix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:01:14