Mage Titans 2016 - Object(ivly) Thinking

65

Transcript of Mage Titans 2016 - Object(ivly) Thinking

HelloWorld

@JcowieMagentoECGMage-CastsGithub:Jamescowie

BackgroundonOOPInsightintomythinkingInsightintomyworkflowAfun30mins

Objectively

ObjectOrientedProgramming

Messaging

PrinciplesofGOODsoftware

SOLIDUncleBobMartin

SingleResponsibilityPrincipleAclassshouldhaveoneandonlyonereasontochange,meaningthataclassshouldhaveonlyonejob.

OpenClosed

Objectsorentitiesshouldbeopenforextension,butclosedformodification.

/*** @api*/interfaceProductRepositoryInterface{……}

<preferencefor="Magento\Catalog\Api\ProductRepositoryInterface"type="Magento\Catalog\Model\ProductRepository”

/>

InterfaceSegregationPrinciple

Aclientshouldneverbeforcedtoimplementaninterfacethatitdoesn’tuseorclientsshouldn’tbeforcedtodependonmethodstheydonotuse.

DependencyInversionPrinciple

Entitiesmustdependonabstractionsnotonconcretions.Itstatesthatthehighlevelmodulemustnotdependonthelowlevelmodule,buttheyshoulddependonabstractions.

/*** @api*/interfaceProductRepositoryInterface{……}

<preferencefor="Magento\Catalog\Api\ProductRepositoryInterface"type="Magento\Catalog\Model\ProductRepository”

/>

I

Aframeworkisjustoneofthetoolstohelpyoudeveloper“Better”and“faster”

- Symfony documentation

Convenientcode Maintainablecode

Convenientcode Maintainablecode

CoupledcodeMixedresponsibilityBoundtoframeworkHardertotest

Convenientcode Maintainablecode

CoupledcodeMixedresponsibilityBoundtoframeworkHardertotest

DecoupledcodeSeparateddomainFrameworkagnosticEasiertotest

publicfunctionexecute(){if($this->_request->getParam(\Magento\Framework\App\ActionInterface::PARAM_NAME_URL_ENCODED)){return$this->resultRedirectFactory->create()->setUrl($this->_redirect->getRedirectUrl());

}$category=$this->_initCategory();if($category){$this->layerResolver->create(Resolver::CATALOG_LAYER_CATEGORY);$settings=$this->_catalogDesign->getDesignSettings($category);

//applycustomdesignif($settings->getCustomDesign()){$this->_catalogDesign->applyCustomDesign($settings->getCustomDesign());

}

$this->_catalogSession->setLastViewedCategoryId($category->getId());…..

}

Magentocanbeconvenient

classIndexextends\Magento\Framework\App\Action\Action

/**@var \Magento\Framework\View\Result\PageFactory */protected$resultPageFactory;

publicfunction__construct(\Magento\Framework\App\Action\Context$context,\Magento\Framework\View\Result\PageFactory $resultPageFactory

){$this->resultPageFactory =$resultPageFactory;parent::__construct($context);

}

Largenumberofdependencies

\Magento\Framework\App\Action\Context$context,\Magento\Framework\View\Result\PageFactory $resultPageFactory\Magento\Framework\Registry$coreRegistry,\Magento\Store\Model\StoreManagerInterface $storeManager,CategoryRepositoryInterface $categoryRepository

Unittestingbecomeshard

The“convenient”controllerrequiresmoreIntegrationTests

Potentialcodesmell

LetsseeandExample

protectedfunctionconfigure(){$this->setName('generate:showcaseproducefeed');$this->setDescription('Generateaproducefeedofshowcaseproductsinjson');parent::configure();

}

protectedfunctionexecute(InputInterface $input,OutputInterface $output){$product=$this->products->get('24-MB01');$writer=$this->filesystem->getDirectoryWrite('var');$file=$writer->openFile('showcase.json','w');

try{$file->lock();try{$file->write(json_encode(['product_name'=>$product->getName(),

'product_price'=>$product->getPrice()]));

}finally{$file->unlock();

}}finally{$file->close();

}

$output->writeln("FeedGenerated");}

CanweaddXMLfeedgeneration

protectedfunctionexecute(InputInterface $input,OutputInterface $output){$product=$this->products->get('24-MB01');$writer=$this->filesystem->getDirectoryWrite('var');$file=$writer->openFile('showcase.json','w');$xmlFile =$writer->openFile('showcase.xml','w');try{$file->lock();try{$file->write(json_encode(['product_name'=>$product->getName(),

'product_price'=>$product->getPrice()]));

$showcaseXML =new\SimpleXMLElement("<showcase></showcase>");$showcaseXML->addAttribute('showcase-products','today');$showcaseAttributes =$showcaseXML->addChild($product->getName());$showcaseAttributes->addAttribute('price',$product->getPrice());

$xmlFile->write($showcaseXML->asXML());}finally{$file->unlock();

}…$output->writeln("FeedGenerated");

}

Ourclasshadreasontochange

Howcanwetestthis?

publicfunction__construct(\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,\Magento\Framework\App\State$state,\Magento\Framework\Filesystem$filesystem

){$state->setAreaCode('frontend');$this->products=$productRepository;$this->filesystem=$filesystem;parent::__construct();

}

$showcaseXML =new\SimpleXMLElement("<showcase></showcase>");

Scenario:ProductsarepresentedinJSONfileGivenThegeneratecommandexistsWhenIrunthegeneratecommandThenIshouldseeaJSONfilecreated

useBehat\Behat\Context\Context;useSymfony\Component\Process\Process;use\Symfony\Component\Filesystem\Filesystem;

classFeatureContext implementsContext{private$output;private$filesystem;publicfunction__construct(){$this->filesystem=newFilesystem();

}}

/***@Given Thegeneratecommandexists*/publicfunctiontheGenerateCommandExists(){returntrue;

}

/*** @WhenIrunthegeneratecommand*/publicfunctioniRunTheGenerateCommand(){$process=newProcess("php ".getcwd()

."/../../../generate:showcaseproducefeed");$process->run();$this->output=$process->getOutput();

}

/*** @ThenIshouldseeaJSONfilecreated*/publicfunctioniShouldSeeAJsonFileCreated(){expect(file_exists(__dir__.

'/../../../../../../var/showcase.json'))->toBe(true);}

TestsPass

Scenario:ProductsarepresentedinXMLfileGivenThegeneratecommandexistsWhenIrunthegeneratecommandThenIshouldseeaXMLfilecreated

TestsFail

Wecouldaddthisintothecommandbuuuut.

OpenClosedPrinciple

<?php

namespaceTitans\Showcase\Api;

interfaceBuilderInterface{publicfunctionbuild($data);

}

Createascenarioforextraction

Scenario:ClassfiledoesreturnjsonWhenIcalltheJSONBuilder classThenIshouldbereturnedajson string

publicfunction__construct(){$this->builder=

newTitans\Showcase\Commands\Builders\JsonBuilder();}

/*** @WhenIcalltheJSONBuilder class*/publicfunctioniCallTheJsonbuilderClass(){$this->output=$this->builder->build(['name'=>'james']);

}

/*** @Then Ishouldbereturnedajson string*/publicfunctioniShouldBeReturnedAJsonString(){expect($this->output)->toBe(json_encode(['name'=>'james']));

}

<?php

namespaceTitans\Showcase\Commands\Builders;

classJsonBuilder implements\Titans\Showcase\Api\BuilderInterface{publicfunctionbuild($data){returnjson_encode($data);

}}

<?php

namespaceTitans\Showcase\Commands\Builders;

classJsonBuilder implements\Titans\Showcase\Api\BuilderInterface{publicfunctionbuild($data){returnjson_encode($data);

}}

<?php

namespaceTitans\Showcase\Commands\Builders;

classXmlBuilder implements\Titans\Showcase\Api\BuilderInterface{publicfunctionbuild($data){return….

}}

publicfunction__construct(\Titans\Showcase\Commands\Builders\JsonBuilder $jsonBuilder

){$this->jsonBuilder =$jsonBuilder;parent::__construct();

}

protectedfunctionexecute(InputInterface $input,OutputInterface$output){$product=$this->products->get('24-MB01');$this->writer->write(

'showcase.json',$this->jsonBuilder->build(

['product_name'=>$product->getName()])

);}

CommandClass

BuilderClass

SendsMessage

CommandClass Responsibilityforhandlingthecommand

JSONBuilderClass ResponsibilityBuildingJSONbasedonamessage

ValueObject.

CommandClass

BuilderClass

IntegrationTestsFrameworkcode

UnitTestsLibrarycode

Whatarethebenefits?WenowpassmessagesbetweenobjectsWearetestingtosomelevelusingBehatWehaveclearObjectsWeuseValueObjects