ooad1-11 Exercise 1: Why the Detector Found Two Groups and a Person Finds Five Jobs ===================================================================================== ex1.py (needs smells.py and flawed_library.py from the chapter folder; run from there): """Exercise 1: run the smell detector on the flawed module, then compare its verdict with your own reading. Run from the chapter folder: py ex1/ex1.py""" import pathlib import sys HERE = pathlib.Path(__file__).resolve().parent.parent sys.path.insert(0, str(HERE)) import smells # noqa: E402 path = str(HERE / "flawed_library.py") for line in smells.analyse(path): print(line) print() print("the jobs a person sees in LibraryManager (by reading the method comments):") jobs = {"lending": ["borrow", "return_book"], "e-mail": ["send_email"], "reporting": ["build_report", "print_report"], "persistence": ["save", "load"], "searching": ["index_title", "search"]} for job, methods in jobs.items(): print(f" {job:<12}{', '.join(methods)}") n, groups = smells.lcom4(smells.classes_of(path)["LibraryManager"]) print(f"\nthe detector found {n} groups; a reader finds {len(jobs)} jobs.") print("why it merged them: the jobs share fields (members: borrow and save; fines: return_book and build_report) and return_book calls send_email.") Run with: py ex1/ex1.py Output: GOD CLASS LibraryManager: 9 methods in 2 unrelated groups: {borrow, return_book, send_email, build_report, print_report, save, load}; {index_title, search} MUTUAL Member and Loan each name the other ANEMIC? 2 of 5 classes hold only data (Copy, Fine); LibraryManager reads or writes their fields 7 times from outside the jobs a person sees in LibraryManager (by reading the method comments): lending borrow, return_book e-mail send_email reporting build_report, print_report persistence save, load searching index_title, search the detector found 2 groups; a reader finds 5 jobs. why it merged them: the jobs share fields (members: borrow and save; fines: return_book and build_report) and return_book calls send_email. HOW I WOULD SPLIT LibraryManager Lending keeps borrow and return_book; it is the domain behaviour and belongs with Member, Loan and Copy (chapter 6's Member.borrow and Member.give_back) Fines the rule becomes a FinePolicy (a Strategy); the rule 'J cards pay 5p' that hides in Loan.overdue_fine moves out of the Loan E-mail a Subscriber that listens for 'book returned with a fine'; return_book stops calling send_email Reporting a ReportBuilder that takes the fines it should report on as an argument; no report_lines field on the manager Persistence a Catalogue interface owned by the application, with a JSON provider (chapter 9); save and load disappear from the manager Search a CatalogueIndex class with index_title and search; it shares no data with lending, which is why the detector did separate this one The interfaces between them: Catalogue (required by the lending code), Subscriber (required by whatever announces events), FinePolicy (required by the member). WHY THIS WORKS AS AN ANSWER --------------------------- The detector linked methods that touch the same field or call each other. send_email is called by return_book, build_report reads the fines that return_book writes, and save and load use the member list that borrow uses. Those links are real, and they are the symptoms of the problem, not evidence that the jobs belong together: the manager owns all the data, so every job reaches into it. The count of two is therefore a lower bound. The detector is useful for pointing at a class; reading the method names finds the jobs.