Prato NV
							
								Wouter Groeneveld & Luk Weyens
								30/09/2019
							
						
					
GSM Uit aub
						Verstand Aan aub
						Vragen stellen toegelaten!
					
Clean code is code that is easy to understand and easy to change.
			function process($obj, $continue = FALSE) {
				// echo "starting the process";
				if($obj->generate() == NULL) {
					return -1;
				}
				$result = $obj->generate();
				if($continue) {
					return $result->generate();
				}
				return $result;
			}
						
					
	function getBookByISBN($isbn) {
		$book = array_search($isbn, $this->books);
		if($book == FALSE) {
			throw new BookNotFoundException("book was not found!");
		}
		return $book;
	}
						
					
					Code gets read a lot (at least whenever someone is writing more code), so any school of clean code should emphasize readability. Cleaning up a little wherever you go is required to keep code clean.
				
$result = $processor->process();
					
					VS
					
$order = $library->checkOut();
					
				
					
	/**
	 *
	 * Prolong the book
	 *
	 * @param    Book  		$i The book to prolong
	 * @param    DateTime  		$j The date to prolong to
	 * @param    Person		$k The person to prolong the book to
	 * @return   nothing if OK
	 *
	 */
	function prolong($i, $j, $k) {
		//Check if this book has been checked out by the same person
		if($i->person != $k)
			//BUSTED!
			return "This is not a book you checked out."
		else
			//Everything OK, prolong the book
			$book->checkOutDate = $date;
	}
						
					
	function prolong($book, $date, $person) {
		if(!$book->isCheckedOutBy($person))
			return "This is not a book you checked out."
		else
			$book->checkOutDate = $date;
	}
						
					
class Book {
	function prolong($date, $person) {
		// does only one thing: prolonging the book
	}
}
										
				
function checkoutBook($title, $isbn, $person, $enoughCredit = FALSE) {
	// ...
}
					
					VS
					
class Person {
	function checkoutBook($book) {
		// ...
	}
}
															
				
	class Book {
			function prolong($date, $person) {
				//prolong implementatie
				if($this->isOverDue()){
					$person->credit -= 10;
				}
		}
	}
						
					
	class Book {
		function prolong($date, $person) {
			//prolong implementatie
		}
	}
	if($book.isOverdue($date)){
		$person->applyFine();
	}
	$book->prolong($date, $person);
																					
					
						http://phpunit.de/manual/
					
	public class BookRepository {
		public $db;
		public $bookCache = [];
	}
	$bookRepositoryInstance->db->query('where isOverdue = TRUE');
						
					
public class BookRepository {
	private $db;
	public function getOverdueBooks() {
		return $db->query('where isOverdue = TRUE');
	}
}
$bookRepositoryInstance->getOverdueBooks();
						
					
$checkoutDate = NULL;
function isOverdue($book) {
	global $checkoutDate;
	// some domain logic here
	$checkoutDate = new Date();
}
$book->setCheckoutDate($checkoutDate); 
					
					Zie ook static e.a.
	$bookid = $_POST['bookid'];
	function prolong($book, $date, $person) {
			$book->checkOutDate = $date;
			$person->bookList->push($book);
		}
	}
	prolong($db->getby($bookid), $_POST['date'], $person);
	echo "thank you it has been prolonged"
						
					
	class Book {
		function prolong($date, $person) {
			$person->addToBookList($this);
		}
	}
	$book = new Book();
	$book->prolong($_POST['date'], $person);
	$view->renderThankYouPage();
											
						
	class BookTest extends TestCase {
		public void testProlongAddsToPersonBookList() {
			// ...
		}
	}
						
					
	function getAisle($book) {
		switch($book->type) {
			case 'nonficition': return 400;
			case 'fiction': return 234;
		}
	}
	getAisle({ type: 'nonfiction' });
						
					
	abstract class Book {
		abstract public function getAisle();
	}
	class FictionBook extends Book {
		function getAisle() {
			return 234;
		}
	}
	class NonFictionBook extends Book {
		function getAisle() {
			return 400;
		}	
	}
	new NonFictionBook()->getAisle();
											
					
$repository->getBookById(124)->getAuthor()->getAllBooks()->filterByName('train');
					
				
	function getBookById($id) {
		if($id == NULL) return -1;
		return $db->getById($id);
	}
						
					
function getBookById($id) {
	if($id == NULL) throw new InvalidIdException("id cannot be null!");
	return $db->getById($id);
}
						
					Structuur veranderen, zonder inhoud te wijzigen!
				
class Dierentuin {
	public function bezoek() {
		// return [] of dieren
	}
	public function ontvangDier($dier) {
		// ??
	}
}
						
					
class Dierentuin {
	public function voeder($voedsel) {
		return TRUE;	// ??
	}
}
						
					
	public function voeder($voedsel) {
		// wat nu??
	}
						
					
class Dier {
	public function isAllergischAan($voedsel) {
		return TRUE;	// ??
	}
}
						
					You cannot write perfect software. Therefore, do not waste energy trying;
be pragmatic.
"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."
				
					
				
				
				
	public function testFormula_evaluatesSimpleCalculation() {
		$formula = "3+5";
		$this->assertEquals(8, eval('return ' . $formula . ';'));
	}
						
						Gebruik nooit eval() in productiecode!
				Programmers are really authors, and your target audience is not the computer it is other programmers (and yourself).
	function render() {
		renderRest();
	}
	function renderRest() {
		document.getElementById('#ak').innerHTML = 'Hello PXL!';
	}
					
					VS 
					
	function renderHelloPXLPage() {
		document.getElementById('#content').innerHTML = 'Hello PXL!';
	}
					
				
	function getBodyElements() {
		return document.querySelector('.body').forEach(function(i) {
			bodies++;	// ???
		});
	}
					
					VS
					
	function getBodyElements() {
		return documents.querySelector('.body');
	}
	function getAmountOfBodyElements() {
		return getBodyElements().length;
	}
					
				
	function prolongBook() {
		if(arguments[1]) {
			var overdue = true;
		}
		if(arguments[0]) {
			arguments[0].prolong(overdue);
		}
	}
					
					VS
					
	function prolongBook(book, isOverdue) {
		// ...
	}
					
				
	function renderContentCallback() {
		if(!this.rendered) {
			reRenderContent();
		}
		this.rendered = true;
	}
					
					Zelfde principe in elke taal...
				
	var obj = {
		isRendered: false,
		render: function() {
			if(!this.isRendered) {
				refresh();
			}
			this.isRendered = true;
		}
	};
						
						Is isRendered voldoende afgeschermd?
					
	obj = (function() {
		var isRendered = false;
		var render = function() {
			// ...
		}
		return {
			render: render
		};
	})();
						
					
	(function() {
		// niemand kan aan mij! FUNCTION LEVEL SCOPE
	})();
						
						Expose enkel wat jij wil als publieke interface:
						
	myAPI = (function() {
		return {
			// enkel DIT is aanspreekbaar
		};
	})();
						
					
	function main() {
	    if (TRUE) {
	        $i = 3;
	    }
	    echo $i;
	}
	main();			// wat doet dit?
						
						Waarom?
					
	var main = function () {
	    if (true) {
	        var i = 3;
	    }
	    console.log(i);
	}
	main();			// wat doet dit?
    				
    				wordt door interpreter dit:
						
	var main = function () {
		var i = undefined;
	    if (true) {
	        i = 3;
	    }
	    console.log(i);
	}
    				
                    
	void main() {
	    if (true) {
	        int i = 3;
	    }
	    printf("%d", i);	// wat doet dit?
	}
						
						Waarom? Statisch getypeerd!
					
	function calculateBookPrice() {
		discount = 10;	// oops??
		return this.basePrice + calculateOverduePrice() - discount;
	}
					
					Wat is de waarde van window.discount?
				
					
VS
					
	
  
					
					
					Pear PHP standaarden:
	var bookid = document.querySelector('#bookid').innerHTML;
	var prolong = function(book, date, person) {
			book.checkOutDate = date;
			person.bookList.push(book);
		}
	};
	doAjaxCall('getBookById.json', bookid, function(book) {
		prolong(book, someDate, person);
	});
	document.querySelector('#messages').innerHTML = "thank you";
						
						Wat gaat er mis?
					
	// ------ domain/book.js ------ 
	var Book = {
		prolong: function(date, person) {
			person.addToBookList(this);
		}
	};
	// ------ controller/bookid.js ------
	var bookid = view.getBookId();
	doAjaxCall('getBookById.json', bookid, function(book) {
		book.prolong(someDate, person);
	});
	view.renderThankYouPage();
						
						Wat gaat er mis?
					
	doAjaxCall('getBookById.json', bookid, function(book) {
		book.prolong(someDate, person);
		view.renderThankYouPage();
	});
						
					
	describe("Book tests", function() {
	  it("should add book to person list", function() {
	  	var book = Object.create(Book);
	  	book.prolong(someDate, person);
	    expect(person.getBookList()).toContain(book);
	  });
	});						
						
					Héél belangrijk in goed onderhoudbare JS code:
Moeilijk testbare code => beter eerst testen geschreven??
		describe("A suite", function() {
		  it("contains spec with an expectation", function() {
		    expect(true).toBe(true);
		  });
		});
						
						Beschrijft gedrag
					
					
class Dierentuin {
	constructor() {}
	bezoek() {
		// return [] of dieren
	}
	ontvangDier(dier) {
		// ??
	}
}
						
					
class Dierentuin {
	voeder(voedsel) {
		return true;	// ??
	}
}
						
					
	public function voeder($voedsel) {
		// wat nu??
	}
						
					
class Dier {
	public function isAllergischAan($voedsel) {
		return TRUE;	// ??
	}
}
						
					Van PHP naar JS met ES6
JS VS PHP:
class Dier {
	constructor(arg) { }		// __construct()
	eet(voedsel) {			// public function eet($voedsel)
		this.boer();		// $this->boer();
	}
	boer() {			// public function boer()
		console.log('burp'); 	// echo "burp";
	}
}
new Dier().eet();			// new Dier()->eet();
						
						
						Herwerk je code!Eerst leren kruipen, dan lopen!
				If a feature is sometimes useful and sometimes dangerous and if there is a better option then always use the better option.Code Quality tools demo @ JSLint voor:
		"use strict";
		function bla() {
		  mwaha = "hi";
		  if(mwaha == "hi") {
		    console.log("hi there too")
		  }
		}
					
				
	var Book = {
		prolong: function(person) {
			var date = Date.now();
			this.dbHandle.prolong(date, person.id);
		}
	};
						
						Ik heb dbHandle.prolong() apart getest, wat nu?
					Injecteer een andere dbHandle: een "mock".
 	var book = Object.create(Book);
 	book.dbHandle = {
 		prolong: function(date, personId) {
 			// Captured arguments! Assert stuff here
 		}
 	};
						
					
						Laat Jasmine "spies" aanmaken
						https://jasmine.github.io/api/2.6/global.html#spyOn
						
it("should call prolong from repository", () => {
	let personId;
	const dbHandle = spyOn('dbHandle', 'prolong').and.callFake((theDate, thePersonId) => {
		personId = thePersonId;
	});
	book.dbHandle = dbHandle;
	book.prolong(person);
	expect(personId).toBe(person.id);
});
						
					https://phpunit.de/manual/current/en/test-doubles.html
    public function testStub()
    {
        $mock = $this->createMock(DbHandle::class);
        $mock->method('prolong')
        		->with($this->anything())
        		->with($this->person.id);
    	$book = new Book($mock);
        $this->assertEquals('foo', $book->prolong());
    }
						
					
						Welke stukken kan ik op die manier testen...
						in een Symfony project?
						
						
      $bookRepositoryMock = $this->getMockBuilder(EntityRepository::class)
            ->disableOriginalConstructor()
            ->getMock();
      $bookRepositoryMock->expects($this->once())
            ->method('find')
            ->will($this->returnValue($book));
     $bookController = new BookController($bookRepositoryMock);
						
						http://symfony.com/doc/current/testing/database.html
					"Programmers should avoid writing code that looks like dog.getBody().getTail().wag();
The solution is to rewrite our example as dog.expressHappiness();
and let the implementation of the dog decide what this means."
Don't
Repeat
Yourself!
						Anders gezegd zijnde: 
						"Duplication is the root of all evil"
					
You
Aint
Gonna
Need
It
						Anders gezegd zijnde: 
						Schrap "Als dit er ooit komt en in geval van x..."
					
http://www.prato-services.eu/![]()