Jak vyřešit kruhovou závislost?
On 1 ledna, 2021 by adminMám tři třídy, které jsou vzájemně závislé:
TestExecuter provádí požadavky TestScenario a uloží soubor sestavy pomocí třídy ReportGenerator . Takže:
- TestExecuter závisí na ReportGenerator pro generování zprávy
- ReportGenerator závisí na TestScenario a na parametrech nastavených z TestExecuter.
- TestScenario závisí na TestExecuter.
Nelze přijít na to, jak odstranit tyto závislosti.
public class TestExecuter { ReportGenerator reportGenerator; public void getReportGenerator() { reportGenerator = ReportGenerator.getInstance(); reportGenerator.setParams(this.params); /* this.params several parameters from TestExecuter class example this.owner */ } public void setTestScenario (TestScenario ts) { reportGenerator.setTestScenario(ts); } public void saveReport() { reportGenerator.saveReport(); } public void executeRequest() { /* do things */ } }
public class ReportGenerator{ public static ReportGenerator getInstance(){} public void setParams(String params){} public void setTestScenario (TestScenario ts){} public void saveReport(){} }
public class TestScenario { TestExecuter testExecuter; public TestScenario(TestExecuter te) { this.testExecuter=te; } public void execute() { testExecuter.executeRequest(); } }
public class Main { public static void main(String [] args) { TestExecuter te = new TestExecuter(); TestScenario ts = new TestScenario(te); ts.execute(); te.getReportGenerator(); te.setTestScenario(ts); te.saveReport() } }
EDIT: v reakci na odpověď, více podrobností o mé třídě TestScenario:
public class TestScenario { private LinkedList<Test> testList; TestExecuter testExecuter; public TestScenario(TestExecuter te) { this.testExecuter=te; } public void execute() { for (Test test: testList) { testExecuter.executeRequest(test); } } } public class Test { private String testName; private String testResult; } public class ReportData { /*shall have all information of the TestScenario including the list of Test */ }
Příklad xml soubor, který má být vygenerován v případě scénáře obsahujícího dva testy:
<testScenario name="scenario1"> <test name="test1"> <result>false</result> </test> <test name="test1"> <result>true</result> </test> </testScenario >
Komentáře
Odpovědět
Technicky můžete vyřešit jakoukoli cyklickou závislost pomocí rozhraní, jak je znázorněno v ostatních odpovědích. Doporučuji však přehodnotit svůj design. Myslím, že je nepravděpodobné, že se vyhnete potřebě dalších rozhraní úplně, zatímco váš design bude ještě jednodušší.
Myslím, že není nutné, aby ReportGenerator
závisel přímo na TestScenario
. Zdá se, že TestScenario
má dvě odpovědnosti: používá se k provádění testu a funguje také jako kontejner výsledků. Jedná se o porušení SRP. Je zajímavé, že vyřešením tohoto narušení se také zbavíte cyklické závislosti.
Takže místo toho, abyste generátoru sestav nechali data ze zkušebního scénáře, předejte data explicitně pomocí nějakého hodnotového objektu. To znamená, nahradit
reportGenerator.setTestScenario(ts);
nějakým kódem, jako je
reportGenerator.insertDataToDisplay(ts.getReportData());
Metoda getReportData
musí mít návratový typ jako ReportData
, hodnotový objekt, který funguje jako kontejner pro data, která se mají zobrazit v sestavě. insertDataToDisplay
je metoda, která očekává objekt přesně tohoto typu.
Tímto způsobem ReportGenerator
a TestScenario
bude oba záviset na ReportData
, což nezávisí na ničem jiném, a první dvě třídy už na sobě nezávisí.
Jako druhý přístup: k vyřešení porušení SRP nechejte TestScenario
zodpovědnost za uchovávání výsledků provádění testu, nikoli však za volání testovacího spouštěče. Zvažte reorganizaci kódu, aby testovací scénář nepřistupoval k prováděcímu programu testu, ale spouštěcí program se spouští zvenčí a zapisuje výsledky zpět do TestScenario
objektu. V příkladu, který jste nám ukázali, to bude možné zpřístupněním LinkedList<Test>
uvnitř TestScenario
veřejnosti a přesunutím execute
metoda z TestScenario
někam jinam, možná přímo do TestExecuter
, možná do nové třídy TestScenarioExecuter
.
Takto bude TestExecuter
záviset na TestScenario
a ReportGenerator
, ReportGenerator
bude také záviset na TestScenario
, ale TestScenario
nebude záviset na ničem jiném.
A konečně, třetí přístup: TestExecuter
má také příliš mnoho odpovědností. Je odpovědný za provádění testů a za poskytování TestScenario
do ReportGenerator
. Dejte tyto dvě odpovědnosti do dvou samostatných tříd a vaše cyklická závislost znovu zmizí.
Existuje více variant řešení vašeho problému, ale doufám, že získáte obecnou představu: vaším hlavním problémem jsou třídy s příliš mnoho odpovědností . Vyřešte tento problém a cyklické závislosti se zbavíte automaticky.
Komentáře
- Díky za vaši odpověď, vlastně potřebuji všechny informace v TestScenario abych mohl na konci vygenerovat můj přehled 🙁
- @ sabrina2020: a co vám brání v uvedení všech těchto informací do
ReportData
?Můžete zvážit úpravu své otázky a trochu podrobněji vysvětlit, co se děje uvnitřsaveReport
. - Vlastně můj TestScenario obsahuje seznam Testů a chci vše informace v souboru XML sestavy, takže ReportData bude mít v tomto případě vše, upravím svou odpověď pro další podrobnosti, díky!
- +1: Měli jste mě na
interfaces
. - @ sabrina2020: K odpovědi jsem přidal dva různé přístupy, vyberte ten, který nejlépe vyhovuje vašim potřebám.
Odpověď
Pomocí rozhraní můžete vyřešit kruhovou závislost.
Aktuální návrh:
Navrhovaný design:
V navrhovaném konkrétním návrhu třídy nezávisí na jiných konkrétních třídách, ale pouze na abstrakcích (rozhraních).
Důležité:
Musíte použít kreační vzor podle vašeho výběru (možná továrna), abyste se vyhnuli provedení new
libovolných konkrétních tříd uvnitř jakékoli jiné konkrétní třídy nebo voláním getInstance()
. Pouze továrna bude mít závislosti na konkrétních třídách. Vaše Main
třída by mohla sloužit jako továrna, pokud si myslíte, že specializovaná továrna by byla přehnaná. Například můžete ReportGenerator
vložit TestExecuter
místo volání getInstance()
nebo new
.
Odpověď
Protože TestExecutor
používá pouze ReportGenerator
interně, měli byste být schopni definovat rozhraní a odkazovat na rozhraní v TestScenario
. Potom TestExecutor
závisí na ReportGenerator
, ReportGenerator
závisí na TestScenario
a TestScenario
závisí na ITestExecutor
, což nezávisí na ničem.
Ideálně vy „Definovat rozhraní pro všechny vaše třídy a vyjadřovat prostřednictvím nich závislosti, ale toto je ta nejmenší změna, která váš problém vyřeší.
File(filename).write(Report); Report = XMLResult(ResultData).toString(); ResultData = TestSuite(SingleTestLogic).execute(TestDataIterator(TestDetailsList))