SlideShare uma empresa Scribd logo
1 de 42
TDD
and
OO
Design
Lessons
from
 We
value
code

  
 that
is
easy
to
maintain

 over
code

  
 that
is
easy
to
write
Some
Design
Principles

   Not
meant
to
be
comprehensive




                                    3
   Coupling
and
Cohesion:
     Loose
coupling

easier
maintenance
     Cohesion:
unit
of
responsibility
   Roles,
Responsibilities,
Collaborators:
     Role:
set
of
related
responsibilities
     Collaborators:
roles
you
interact
with
   Internals
vs.
Peers
   No
And’s,
Or’s,
or
But’s
     Every
object
should
have
a
single,
clearly
defined

      responsibility
   Three
types
of
Peers:
     Dependencies
      ▪ The
object
can’t
live
without
them
     Notifications
      ▪ Need
to
be
kept
up
to
date,
but
we
“don’t
care”
if
they

        listen
     Adjustments
   New
or
new
not.
There
is
no
try.
(Yoda)

 Dependencies
have
to
be
passed
into
the

  constructor.
 Notifications
and
Adjustments
can
be

  initialised
to
safe
defaults.
   Identify
Relationships
with
Interfaces
   Keep
interfaces
narrow
   Interfaces
are
pulled
into
existence
by
tests
   No
I<class>
(ICustomer 
Customer)
   No
InterfaceImpl
(Customer 
CustomerImpl)
   Name
each
implementation!
   If
you
don’t
find
a
good
name
for
the

    implementation,
maybe
it’s
not
a
good

    interface…
Important
Design
Principle
Use
with
Objects,
not
People
:‐)
   Try
to
understand
this
(train
wreck):
((EditSaveCustomizer) master
.getModelisable()
  .getDockablePanel()
    .getCustomizer())
      .getSaveItem()

 
        .setEnabled(Boolean.FALSE.booleanValue());

   Isn’t
this
simpler?
master.allowSavingOfCustomisations();
   Or,
now
really
with
a
train:
public class Train {
  private final List<Carriage> carriages […]
  private int percentReservedBarrier = 70;
  public void reserveSeats(ReservationRequest request) {
    for (Carriage carriage : carriages) {
      if (carriage.getSeats().getPercentReserved() <
    
      
       
       
        
      percentReservedBarrier) {
        request.reserveSeatsIn(carriage);
        return;
      }
    }
    request.cannotFindSeats();
  }
}
   Why
isn’t
that
good
design?
   Isn’t
this
simpler?
public void reserveSeats(ReservationRequest request) {
  for (Carriage carriage : carriages) {
    if (carriage.hasSeatsAvailableWithin

 
       
       
       (percentReservedBarrier)) {
      request.reserveSeatsIn(carriage);
      return;
    }
  }
  request.cannotFindSeats() ;
}
General
Tips
on
Testing

   How
to
make
it
simpler...




                                12
Phrase
Messages
with
Meaning

   What
went
wrong
here?
     Found
<null>
expected
<not
null>
     Found
<0>
expected
<17>
 Use
constants
 Use
special
types
     Found
<Harry>
expected
<Customer.NotFound>




                                                   13
Be
Literal

 Numbers,
Strings,
…
are
hard
to
understand
 Better:
     Constants
     Enumerations
     Value
Types
   There
is
no
“too
small”
for
types



                                               14
Beware
“Long”
Strings!

   Test
format
and
contents
independently
   “13/09/10
–
Order
8715
signed
by
Manfred
Mayer”
   Define
a
Value
Type
for
longer
Messages
   “<decision.date>
‐
Order
    
<decision.no>
signed
by
<decision.owner>”
   Public
class
Decision{
     Public
Date
date;
     Public
String
no;
     Public
String
owner;
   }
                                                  15
Use
Assertions
sensibly

   Assert
one
expected
behaviour
in
exactly
one

    test
     Otherwise
you
have
to
regularly
update
multiple

      tests!
     Otherwise
it’s
much
less
clear
what
the
test
does
 Rather
have
one
more
test
than
an
unclear

  one
 The
“Single
Responsibility
Principle”
applies

  here,
too!
                                                          16
Working
with
Test
Data

   How
to
make
your
life
easier




                                   17
   Many
attempts
to
communicate
are
nullified

    by
saying
too
much.—
Robert
Greenleaf
   First
Try
@Test public void chargesCustomerForTotalCostOfAllOrderedItems() {
  Order order = new Order(
    new Customer("Sherlock Holmes",
       new Address("221b Baker Street",
              "London",
              new PostCode("NW1", "3RX"))));
  order.addLine(new OrderLine("Deerstalker Hat", 1));
  order.addLine(new OrderLine("Tweed Cape", 1));
[…]
}
Test
Data‐by
hand?

 Complex,
confusing
 Hard
to
write
 Error‐prone




                       20
   Centralise
test
data
creation…

Order order1 =
  ExampleOrders.newDeerstalkerAndCapeAndSwordstickOrder();
Order order2 =
  ExampleOrders.newDeerstalkerAndBootsOrder();
[…]
 Test
are
clearer,
but...
 Complexity
and
repetition
just
moved
some

  place
else!
 Hide
defaults
 Show
specialties
public class OrderBuilder {
  private Customer customer = new CustomerBuilder().build();
  private List<OrderLine> lines = new ArrayList<OrderLine>();
  private BigDecimal discountRate = BigDecimal.ZERO;
  public static OrderBuilder anOrder() {
    return new OrderBuilder();
  }
  public OrderBuilder withCustomer(Customer customer) {
    this.customer = customer;
    return this;
  }
  public OrderBuilder withOrderLines(List<OrderLine> lines) {
    this.lines = lines;
    return this;
  }
  public OrderBuilder withDiscount(BigDecimal discountRate) {
    this.discountRate = discountRate;
    return this;
  }
  public Order build() {
    Order order = new Order(customer);
    for (OrderLine line : lines) order.addLine(line);
    
 order.setDiscountRate(discountRate);
    }

      return order;
  }
}
public class OrderBuilder {
  private Customer customer = new CustomerBuilder().build();    Default
values
  private List<OrderLine> lines = new ArrayList<OrderLine>();
  private BigDecimal discountRate = BigDecimal.ZERO;
  public static OrderBuilder anOrder() {
    return new OrderBuilder();
  }
  public OrderBuilder withCustomer(Customer customer) {
    this.customer = customer;
    return this;
  }
  public OrderBuilder withOrderLines(List<OrderLine> lines) {
    this.lines = lines;
    return this;
  }
  public OrderBuilder withDiscount(BigDecimal discountRate) {
    this.discountRate = discountRate;
    return this;
  }
  public Order build() {
    Order order = new Order(customer);
    for (OrderLine line : lines) order.addLine(line);
    
 order.setDiscountRate(discountRate);
    }

      return order;
  }
}
public class OrderBuilder {
  private Customer customer = new CustomerBuilder().build();          Default
values
  private List<OrderLine> lines = new ArrayList<OrderLine>();
  private BigDecimal discountRate = BigDecimal.ZERO;
  public static OrderBuilder anOrder() {
    return new OrderBuilder();
  }
  public OrderBuilder withCustomer(Customer customer) {
    this.customer = customer;
    return this;                                                Return:
Builder
  }
  public OrderBuilder withOrderLines(List<OrderLine> lines) {
    this.lines = lines;
    return this;
  }
  public OrderBuilder withDiscount(BigDecimal discountRate) {
    this.discountRate = discountRate;
    return this;
  }
  public Order build() {
    Order order = new Order(customer);
    for (OrderLine line : lines) order.addLine(line);
    
 order.setDiscountRate(discountRate);
    }

      return order;
  }
}
public class OrderBuilder {
  private Customer customer = new CustomerBuilder().build();               Default
values
  private List<OrderLine> lines = new ArrayList<OrderLine>();
  private BigDecimal discountRate = BigDecimal.ZERO;
  public static OrderBuilder anOrder() {
    return new OrderBuilder();
  }
  public OrderBuilder withCustomer(Customer customer) {
    this.customer = customer;
    return this;                                                    Return:
Builder
  }
  public OrderBuilder withOrderLines(List<OrderLine> lines) {
    this.lines = lines;
    return this;
  }
  public OrderBuilder withDiscount(BigDecimal discountRate) {
    this.discountRate = discountRate;
    return this;
  }
  public Order build() {
    Order order = new Order(customer);
    for (OrderLine line : lines) order.addLine(line);
    
 order.setDiscountRate(discountRate);
    }

      return order;                                            build()
always
returns
a
new

  }
}                                                                          order!
   Default
fits
into
one
row:
Order order = new OrderBuilder().build();

   Any
deviation
is
obvious:

new OrderBuilder()
 .fromCustomer(
    new CustomerBuilder()
     .withAddress(new AddressBuilder().withNoPostcode()
   
    
       .build())
     .build())
 .build();
   Creation
using
Object
Mother
hides
the
error:
TestAddresses.newAddress("221b Baker Street", "London", "NW1 6XE");

   With
the
Data
Builder
the
error
is
explicit:
new AddressBuilder()
 .withStreet("221b Baker Street")
 .withStreet2("London")
 .withPostCode("NW1 6XE")
 .build();
   Multiple
objects
lead
to
repetition
(again...):

Order orderWithSmallDiscount = new OrderBuilder()
 .withLine("Deerstalker Hat", 1)
 .withLine("Tweed Cape", 1)
 .withDiscount(0.10)
 .build();
Order orderWithLargeDiscount = new OrderBuilder()
 .withLine("Deerstalker Hat", 1)
 .withLine("Tweed Cape", 1)
 .withDiscount(0.25)
 .build();
   This
is
better
if
objects
differ
in
only
one
field:

OrderBuilder hatAndCape = new OrderBuilder()
 .withLine("Deerstalker Hat", 1)
 .withLine("Tweed Cape", 1);
Order orderWithSmallDiscount =
   hatAndCape.withDiscount(0.10).build();
Order orderWithLargeDiscount =
   hatAndCape.withDiscount(0.25).build();
   Attention,
possible
error:

Order orderWithDiscount = hatAndCape.withDiscount(0.10) .build();
Order orderWithGiftVoucher =
  hatAndCape.withGiftVoucher("abc").build();

   The
second
order
has
a
discount
as
well!
   Better:
    We
add
a
CopyConstructor
to
the
Builder:
Order orderWithDiscount = new OrderBuilder(hatAndCape)
 .withDiscount(0.10)
 .build();
Order orderWithGiftVoucher = new OrderBuilder(hatAndCape)
 .withGiftVoucher("abc")
 .build();
   More
elegant:
    Factory‐Method,
naming
what
it’s
used
for:
Order orderWithDiscount = hatAndCape.

 but().withDiscount( 0.10).build();
Order orderWithGiftVoucher =
  hatAndCape.but().withGiftVoucher("abc").build();
   instead
of:
Order orderWithNoPostcode = new OrderBuilder()
  .fromCustomer(new CustomerBuilder()
      .withAddress(new AddressBuilder()
                                       
    .withNoPostcode()
.build()).build()).build();
   it’s
more
elegant,
to
pass
the
Builder:
Order order = new OrderBuilder()
 .fromCustomer( new CustomerBuilder()
    .withAddress(new AddressBuilder().withNoPostcode() ))).build();
   Even
more
elegant:
Factory
methods
Order order =
anOrder().fromCustomer(
aCustomer().withAddress(anAddress().withNoPostcode() ) ) .build();
   Overloading
makes
it
shorter:
Order order =
   anOrder() .from(aCustomer().with(anAddress().withNoPostcode()))
.build();
   This
is
how
readable
a
test
can
(and
should)

    be.
@Test public void reportsTotalSalesOfOrderedProducts() {
  Order order1 = anOrder()
   .withLine("Deerstalker Hat", 1)
   .withLine("Tweed Cape", 1)
   .withCustomersReference(1234)
   .build();
  requestSender.send(order1);
  progressMonitor.waitForCompletion(order1);
  Order order2 = anOrder()
   .withLine("Deerstalker Hat", 1)
   .withCustomersReference(5678)
   .build();
  requestSender.send(order2);
  progressMonitor.waitForCompletion(order2);
  TotalSalesReport report = gui.openSalesReport();
  report.checkDisplayedTotalSalesFor("Deerstalker Hat", is(equalTo(2) ));
  report.checkDisplayedTotalSalesFor("Tweed Cape", is(equalTo(1)));
}
@Test public void reportsTotalSalesOfOrderedProducts() {
submitOrderFor("Deerstalker Hat", "Tweed Cape");
submitOrderFor("Deerstalker Hat");
  TotalSalesReport report = gui.openSalesReport();
  report.checkDisplayedTotalSalesFor("Deerstalker Hat", is(equalTo(2) ));
  report.checkDisplayedTotalSalesFor("Tweed Cape", is(equalTo(1)));
}
void submitOrderFor(String ... products) {
  OrderBuilder orderBuilder = anOrder()
    .withCustomersReference(nextCustomerReference());
  for (String product : products) {
    orderBuilder = orderBuilder.withLine(product, 1);
  }
  Order order = orderBuilder.build();
  requestSender.send(order);
  progressMonitor.waitForCompletion(order);
}
   This
won’t
scale
well:
void submitOrderFor(String ... products) { […]
void submitOrderFor(String product, int count,
            String otherProduct, int otherCount) { […]
void submitOrderFor(String product, double discount) { […]
void submitOrderFor(String product, String giftVoucherCode) { […]
   Better,
to
use
the
builder:
@Test public void reportsTotalSalesOfOrderedProducts() {
sendAndProcess(anOrder()
   .withLine("Deerstalker Hat", 1)
   .withLine("Tweed Cape", 1));
sendAndProcess(anOrder()
   .withLine("Deerstalker Hat", 1));
  TotalSalesReport report = gui.openSalesReport();
  report.checkDisplayedTotalSalesFor("Deerstalker Hat", is(equalTo(2) ));
  report.checkDisplayedTotalSalesFor("Tweed Cape", is(equalTo(1)));
}
void sendAndProcess(OrderBuilder orderDetails) {
  Order order = orderDetails
   .withDefaultCustomersReference(nextCustomerReference())
   .build();
  requestSender.send(order);
  progressMonitor.waitForCompletion(order);
}
   Better
names
improve
clarity:
@Test public void reportsTotalSalesOfOrderedProducts() {
havingReceived(anOrder()
    .withLine("Deerstalker Hat", 1)
    .withLine("Tweed Cape", 1));
havingReceived(anOrder()
    .withLine("Deerstalker Hat", 1));
  TotalSalesReport report = gui.openSalesReport();
  report.displaysTotalSalesFor("Deerstalker Hat", equalTo(2));
  report.displaysTotalSalesFor("Tweed Cape", equalTo(1) );
}
   This
is
how
readable
a
test
should
be:
@Test public void
    takesAmendmentsIntoAccountWhenCalculatingTotalSales() {
  Customer theCustomer = aCustomer().build();
havingReceived(anOrder().from(theCustomer)
   .withLine("Deerstalker Hat", 1)
   .withLine("Tweed Cape", 1));
havingReceived(anOrderAmendment().from(theCustomer)
   .withLine("Deerstalker Hat", 2));
  TotalSalesReport report = user.openSalesReport();
  report.containsTotalSalesFor("Deerstalker Hat", equalTo(2));
  report.containsTotalSalesFor("Tweed Cape", equalTo(1) );
}

Mais conteúdo relacionado

Destaque

Descale your organisation oop
Descale your organisation oopDescale your organisation oop
Descale your organisation oop
Olaf Lewitz
 

Destaque (11)

Freiheit vs. Sicherheit: Wieviel Unterdrückung ist hilfreich?
Freiheit vs. Sicherheit: Wieviel Unterdrückung ist hilfreich?Freiheit vs. Sicherheit: Wieviel Unterdrückung ist hilfreich?
Freiheit vs. Sicherheit: Wieviel Unterdrückung ist hilfreich?
 
Which Role does Testing Play in an Agile Development Organisation?
Which Role does Testing Play in an Agile Development Organisation?Which Role does Testing Play in an Agile Development Organisation?
Which Role does Testing Play in an Agile Development Organisation?
 
What is Trust? Was ist Vertrauen? #t4at
What is Trust? Was ist Vertrauen? #t4atWhat is Trust? Was ist Vertrauen? #t4at
What is Trust? Was ist Vertrauen? #t4at
 
Invitation Transforms - ALE Kraków
Invitation Transforms - ALE KrakówInvitation Transforms - ALE Kraków
Invitation Transforms - ALE Kraków
 
Trust and responsibility for project success
Trust and responsibility for project successTrust and responsibility for project success
Trust and responsibility for project success
 
Comment libérer et réinventer les organisations­
Comment libérer et réinventer les organisations­Comment libérer et réinventer les organisations­
Comment libérer et réinventer les organisations­
 
Descale your organisation oop
Descale your organisation oopDescale your organisation oop
Descale your organisation oop
 
Agile: A Courageous Choice (Agile Toronto Keynote)
Agile: A Courageous Choice (Agile Toronto Keynote)Agile: A Courageous Choice (Agile Toronto Keynote)
Agile: A Courageous Choice (Agile Toronto Keynote)
 
Integral Quality Agile Testing Days 2015
Integral Quality Agile Testing Days 2015Integral Quality Agile Testing Days 2015
Integral Quality Agile Testing Days 2015
 
Serious Play: Imaginopedia for Core Process
Serious Play: Imaginopedia for Core ProcessSerious Play: Imaginopedia for Core Process
Serious Play: Imaginopedia for Core Process
 
Booster Partners: Surprisability
Booster Partners: SurprisabilityBooster Partners: Surprisability
Booster Partners: Surprisability
 

Semelhante a Learning from GOOS - work in progress

Share pointtechies linqtosp-andsbs
Share pointtechies linqtosp-andsbsShare pointtechies linqtosp-andsbs
Share pointtechies linqtosp-andsbs
Shakir Majeed Khan
 
SOLID - Not Just a State of Matter, It's Principles for OO Propriety
SOLID - Not Just a State of Matter, It's Principles for OO ProprietySOLID - Not Just a State of Matter, It's Principles for OO Propriety
SOLID - Not Just a State of Matter, It's Principles for OO Propriety
Chris Weldon
 

Semelhante a Learning from GOOS - work in progress (20)

SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
Share pointtechies linqtosp-andsbs
Share pointtechies linqtosp-andsbsShare pointtechies linqtosp-andsbs
Share pointtechies linqtosp-andsbs
 
Solid Software Design Principles
Solid Software Design PrinciplesSolid Software Design Principles
Solid Software Design Principles
 
Typed? Dynamic? Both! Cross-platform DSLs in C#
Typed? Dynamic? Both! Cross-platform DSLs in C#Typed? Dynamic? Both! Cross-platform DSLs in C#
Typed? Dynamic? Both! Cross-platform DSLs in C#
 
Wann soll ich mocken?
Wann soll ich mocken?Wann soll ich mocken?
Wann soll ich mocken?
 
Design patterns for fun and profit
Design patterns for fun and profitDesign patterns for fun and profit
Design patterns for fun and profit
 
Introducing N1QL: New SQL Based Query Language for JSON
Introducing N1QL: New SQL Based Query Language for JSONIntroducing N1QL: New SQL Based Query Language for JSON
Introducing N1QL: New SQL Based Query Language for JSON
 
Combatendo code smells em Java
Combatendo code smells em Java Combatendo code smells em Java
Combatendo code smells em Java
 
Introduction to Linq
Introduction to LinqIntroduction to Linq
Introduction to Linq
 
Linq
LinqLinq
Linq
 
GraphQL - when REST API is not enough - lessons learned
GraphQL - when REST API is not enough - lessons learnedGraphQL - when REST API is not enough - lessons learned
GraphQL - when REST API is not enough - lessons learned
 
MongoDB Meetup
MongoDB MeetupMongoDB Meetup
MongoDB Meetup
 
SOLID Principles
SOLID PrinciplesSOLID Principles
SOLID Principles
 
Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"
Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"
Andriy Slobodyanyk "How to Use Hibernate: Key Problems and Solutions"
 
Ensure code quality with vs2012
Ensure code quality with vs2012Ensure code quality with vs2012
Ensure code quality with vs2012
 
Bad test, good test
Bad test, good testBad test, good test
Bad test, good test
 
Java best practices
Java best practicesJava best practices
Java best practices
 
SOLID - Not Just a State of Matter, It's Principles for OO Propriety
SOLID - Not Just a State of Matter, It's Principles for OO ProprietySOLID - Not Just a State of Matter, It's Principles for OO Propriety
SOLID - Not Just a State of Matter, It's Principles for OO Propriety
 
Geek Sync | Rewriting Bad SQL Code 101
Geek Sync | Rewriting Bad SQL Code 101Geek Sync | Rewriting Bad SQL Code 101
Geek Sync | Rewriting Bad SQL Code 101
 
C# What's next? (7.x and 8.0)
C# What's next? (7.x and 8.0)C# What's next? (7.x and 8.0)
C# What's next? (7.x and 8.0)
 

Mais de Olaf Lewitz

Mais de Olaf Lewitz (20)

How to Use your Emotions - Scrum Gathering Belgrade 2021
How to Use your Emotions - Scrum Gathering Belgrade 2021How to Use your Emotions - Scrum Gathering Belgrade 2021
How to Use your Emotions - Scrum Gathering Belgrade 2021
 
TrustTemenos CAL - Certified Agile Leadership
TrustTemenos CAL - Certified Agile LeadershipTrustTemenos CAL - Certified Agile Leadership
TrustTemenos CAL - Certified Agile Leadership
 
If agile is the solution i want my problem back
If agile is the solution i want my problem backIf agile is the solution i want my problem back
If agile is the solution i want my problem back
 
OOP 2020 Wenn Agil die Lösung ist dann hätte ich gerne mein Problem zurück
OOP 2020 Wenn Agil die Lösung ist dann hätte ich gerne mein Problem zurückOOP 2020 Wenn Agil die Lösung ist dann hätte ich gerne mein Problem zurück
OOP 2020 Wenn Agil die Lösung ist dann hätte ich gerne mein Problem zurück
 
TrustTemenos Certified Agile Leadership
TrustTemenos Certified Agile LeadershipTrustTemenos Certified Agile Leadership
TrustTemenos Certified Agile Leadership
 
Leading with:in Tension Scan-Agile
Leading with:in Tension Scan-AgileLeading with:in Tension Scan-Agile
Leading with:in Tension Scan-Agile
 
School of Product Ownership: Own Your Product as if it Mattered
School of Product Ownership: Own Your Product as if it MatteredSchool of Product Ownership: Own Your Product as if it Mattered
School of Product Ownership: Own Your Product as if it Mattered
 
Leading with/in Tension Agile in the City Bristol
Leading with/in Tension Agile in the City BristolLeading with/in Tension Agile in the City Bristol
Leading with/in Tension Agile in the City Bristol
 
Leading with/in Tension
Leading with/in Tension Leading with/in Tension
Leading with/in Tension
 
Leading with/in Tension - Agile Prague
Leading with/in Tension - Agile Prague Leading with/in Tension - Agile Prague
Leading with/in Tension - Agile Prague
 
Agile Day Riga: How does a Technical Guy become an agile Leader?
Agile Day Riga: How does a Technical Guy become an agile Leader?Agile Day Riga: How does a Technical Guy become an agile Leader?
Agile Day Riga: How does a Technical Guy become an agile Leader?
 
Agile Serbia 2018 - How does a technical guy become an agile leader?
Agile Serbia 2018 - How does a technical guy become an agile leader?Agile Serbia 2018 - How does a technical guy become an agile leader?
Agile Serbia 2018 - How does a technical guy become an agile leader?
 
Organisational Neurobiology and Fitness - Agile 2017
Organisational Neurobiology and Fitness - Agile 2017Organisational Neurobiology and Fitness - Agile 2017
Organisational Neurobiology and Fitness - Agile 2017
 
Agile World - Surprisability
Agile World - SurprisabilityAgile World - Surprisability
Agile World - Surprisability
 
Surprisability (Agile in the City)
Surprisability (Agile in the City)Surprisability (Agile in the City)
Surprisability (Agile in the City)
 
Surprisability - AgileEE 2017
Surprisability - AgileEE 2017 Surprisability - AgileEE 2017
Surprisability - AgileEE 2017
 
Booster Partners - How to Liberate Organisations
Booster Partners - How to Liberate OrganisationsBooster Partners - How to Liberate Organisations
Booster Partners - How to Liberate Organisations
 
Surprisability - Agile Prague Sept 2016
Surprisability - Agile Prague Sept 2016Surprisability - Agile Prague Sept 2016
Surprisability - Agile Prague Sept 2016
 
AgileTransitionDay: Wer ist Agilität und wenn ja, wie viele?
AgileTransitionDay: Wer ist Agilität und wenn ja, wie viele?AgileTransitionDay: Wer ist Agilität und wenn ja, wie viele?
AgileTransitionDay: Wer ist Agilität und wenn ja, wie viele?
 
Integral Quality ATAGTR16
Integral Quality ATAGTR16Integral Quality ATAGTR16
Integral Quality ATAGTR16
 

Último

IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
Enterprise Knowledge
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 

Último (20)

The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 

Learning from GOOS - work in progress

  • 2.  We
value
code
 
 that
is
easy
to
maintain
  over
code
 
 that
is
easy
to
write
  • 3. Some
Design
Principles  Not
meant
to
be
comprehensive 3
  • 4. Coupling
and
Cohesion:  Loose
coupling

easier
maintenance  Cohesion:
unit
of
responsibility  Roles,
Responsibilities,
Collaborators:  Role:
set
of
related
responsibilities  Collaborators:
roles
you
interact
with  Internals
vs.
Peers
  • 5. No
And’s,
Or’s,
or
But’s  Every
object
should
have
a
single,
clearly
defined
 responsibility  Three
types
of
Peers:  Dependencies ▪ The
object
can’t
live
without
them  Notifications ▪ Need
to
be
kept
up
to
date,
but
we
“don’t
care”
if
they
 listen  Adjustments
  • 6. New
or
new
not.
There
is
no
try.
(Yoda)  Dependencies
have
to
be
passed
into
the
 constructor.  Notifications
and
Adjustments
can
be
 initialised
to
safe
defaults.
  • 7. Identify
Relationships
with
Interfaces  Keep
interfaces
narrow  Interfaces
are
pulled
into
existence
by
tests  No
I<class>
(ICustomer 
Customer)  No
InterfaceImpl
(Customer 
CustomerImpl)  Name
each
implementation!  If
you
don’t
find
a
good
name
for
the
 implementation,
maybe
it’s
not
a
good
 interface…
  • 9. Try
to
understand
this
(train
wreck): ((EditSaveCustomizer) master .getModelisable() .getDockablePanel() .getCustomizer()) .getSaveItem() .setEnabled(Boolean.FALSE.booleanValue());  Isn’t
this
simpler? master.allowSavingOfCustomisations();
  • 10. Or,
now
really
with
a
train: public class Train { private final List<Carriage> carriages […] private int percentReservedBarrier = 70; public void reserveSeats(ReservationRequest request) { for (Carriage carriage : carriages) { if (carriage.getSeats().getPercentReserved() < percentReservedBarrier) { request.reserveSeatsIn(carriage); return; } } request.cannotFindSeats(); } }  Why
isn’t
that
good
design?
  • 11. Isn’t
this
simpler? public void reserveSeats(ReservationRequest request) { for (Carriage carriage : carriages) { if (carriage.hasSeatsAvailableWithin (percentReservedBarrier)) { request.reserveSeatsIn(carriage); return; } } request.cannotFindSeats() ; }
  • 12. General
Tips
on
Testing  How
to
make
it
simpler... 12
  • 13. Phrase
Messages
with
Meaning  What
went
wrong
here?  Found
<null>
expected
<not
null>  Found
<0>
expected
<17>  Use
constants  Use
special
types  Found
<Harry>
expected
<Customer.NotFound> 13
  • 14. Be
Literal  Numbers,
Strings,
…
are
hard
to
understand  Better:  Constants  Enumerations  Value
Types  There
is
no
“too
small”
for
types 14
  • 15. Beware
“Long”
Strings!  Test
format
and
contents
independently  “13/09/10
–
Order
8715
signed
by
Manfred
Mayer”  Define
a
Value
Type
for
longer
Messages  “<decision.date>
‐
Order 
<decision.no>
signed
by
<decision.owner>”  Public
class
Decision{  Public
Date
date;  Public
String
no;  Public
String
owner;  } 15
  • 16. Use
Assertions
sensibly  Assert
one
expected
behaviour
in
exactly
one
 test  Otherwise
you
have
to
regularly
update
multiple
 tests!  Otherwise
it’s
much
less
clear
what
the
test
does  Rather
have
one
more
test
than
an
unclear
 one  The
“Single
Responsibility
Principle”
applies
 here,
too! 16
  • 17. Working
with
Test
Data  How
to
make
your
life
easier 17
  • 18. Many
attempts
to
communicate
are
nullified
 by
saying
too
much.—
Robert
Greenleaf
  • 19. First
Try @Test public void chargesCustomerForTotalCostOfAllOrderedItems() { Order order = new Order( new Customer("Sherlock Holmes", new Address("221b Baker Street", "London", new PostCode("NW1", "3RX")))); order.addLine(new OrderLine("Deerstalker Hat", 1)); order.addLine(new OrderLine("Tweed Cape", 1)); […] }
  • 21. Centralise
test
data
creation…
 Order order1 = ExampleOrders.newDeerstalkerAndCapeAndSwordstickOrder(); Order order2 = ExampleOrders.newDeerstalkerAndBootsOrder(); […]
  • 24. public class OrderBuilder { private Customer customer = new CustomerBuilder().build(); private List<OrderLine> lines = new ArrayList<OrderLine>(); private BigDecimal discountRate = BigDecimal.ZERO; public static OrderBuilder anOrder() { return new OrderBuilder(); } public OrderBuilder withCustomer(Customer customer) { this.customer = customer; return this; } public OrderBuilder withOrderLines(List<OrderLine> lines) { this.lines = lines; return this; } public OrderBuilder withDiscount(BigDecimal discountRate) { this.discountRate = discountRate; return this; } public Order build() { Order order = new Order(customer); for (OrderLine line : lines) order.addLine(line); order.setDiscountRate(discountRate); } return order; } }
  • 25. public class OrderBuilder { private Customer customer = new CustomerBuilder().build(); Default
values private List<OrderLine> lines = new ArrayList<OrderLine>(); private BigDecimal discountRate = BigDecimal.ZERO; public static OrderBuilder anOrder() { return new OrderBuilder(); } public OrderBuilder withCustomer(Customer customer) { this.customer = customer; return this; } public OrderBuilder withOrderLines(List<OrderLine> lines) { this.lines = lines; return this; } public OrderBuilder withDiscount(BigDecimal discountRate) { this.discountRate = discountRate; return this; } public Order build() { Order order = new Order(customer); for (OrderLine line : lines) order.addLine(line); order.setDiscountRate(discountRate); } return order; } }
  • 26. public class OrderBuilder { private Customer customer = new CustomerBuilder().build(); Default
values private List<OrderLine> lines = new ArrayList<OrderLine>(); private BigDecimal discountRate = BigDecimal.ZERO; public static OrderBuilder anOrder() { return new OrderBuilder(); } public OrderBuilder withCustomer(Customer customer) { this.customer = customer; return this; Return:
Builder } public OrderBuilder withOrderLines(List<OrderLine> lines) { this.lines = lines; return this; } public OrderBuilder withDiscount(BigDecimal discountRate) { this.discountRate = discountRate; return this; } public Order build() { Order order = new Order(customer); for (OrderLine line : lines) order.addLine(line); order.setDiscountRate(discountRate); } return order; } }
  • 27. public class OrderBuilder { private Customer customer = new CustomerBuilder().build(); Default
values private List<OrderLine> lines = new ArrayList<OrderLine>(); private BigDecimal discountRate = BigDecimal.ZERO; public static OrderBuilder anOrder() { return new OrderBuilder(); } public OrderBuilder withCustomer(Customer customer) { this.customer = customer; return this; Return:
Builder } public OrderBuilder withOrderLines(List<OrderLine> lines) { this.lines = lines; return this; } public OrderBuilder withDiscount(BigDecimal discountRate) { this.discountRate = discountRate; return this; } public Order build() { Order order = new Order(customer); for (OrderLine line : lines) order.addLine(line); order.setDiscountRate(discountRate); } return order; build()
always
returns
a
new
 } } order!
  • 28. Default
fits
into
one
row: Order order = new OrderBuilder().build();  Any
deviation
is
obvious: new OrderBuilder() .fromCustomer( new CustomerBuilder() .withAddress(new AddressBuilder().withNoPostcode() .build()) .build()) .build();
  • 29. Creation
using
Object
Mother
hides
the
error: TestAddresses.newAddress("221b Baker Street", "London", "NW1 6XE");  With
the
Data
Builder
the
error
is
explicit: new AddressBuilder() .withStreet("221b Baker Street") .withStreet2("London") .withPostCode("NW1 6XE") .build();
  • 30. Multiple
objects
lead
to
repetition
(again...): Order orderWithSmallDiscount = new OrderBuilder() .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1) .withDiscount(0.10) .build(); Order orderWithLargeDiscount = new OrderBuilder() .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1) .withDiscount(0.25) .build();
  • 31. This
is
better
if
objects
differ
in
only
one
field: OrderBuilder hatAndCape = new OrderBuilder() .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1); Order orderWithSmallDiscount = hatAndCape.withDiscount(0.10).build(); Order orderWithLargeDiscount = hatAndCape.withDiscount(0.25).build();
  • 32. Attention,
possible
error: Order orderWithDiscount = hatAndCape.withDiscount(0.10) .build(); Order orderWithGiftVoucher = hatAndCape.withGiftVoucher("abc").build();  The
second
order
has
a
discount
as
well!
  • 33. Better: We
add
a
CopyConstructor
to
the
Builder: Order orderWithDiscount = new OrderBuilder(hatAndCape) .withDiscount(0.10) .build(); Order orderWithGiftVoucher = new OrderBuilder(hatAndCape) .withGiftVoucher("abc") .build();
  • 34. More
elegant: Factory‐Method,
naming
what
it’s
used
for: Order orderWithDiscount = hatAndCape. but().withDiscount( 0.10).build(); Order orderWithGiftVoucher = hatAndCape.but().withGiftVoucher("abc").build();
  • 35. instead
of: Order orderWithNoPostcode = new OrderBuilder() .fromCustomer(new CustomerBuilder() .withAddress(new AddressBuilder() .withNoPostcode() .build()).build()).build();  it’s
more
elegant,
to
pass
the
Builder: Order order = new OrderBuilder() .fromCustomer( new CustomerBuilder() .withAddress(new AddressBuilder().withNoPostcode() ))).build();
  • 36. Even
more
elegant:
Factory
methods Order order = anOrder().fromCustomer( aCustomer().withAddress(anAddress().withNoPostcode() ) ) .build();  Overloading
makes
it
shorter: Order order = anOrder() .from(aCustomer().with(anAddress().withNoPostcode())) .build();  This
is
how
readable
a
test
can
(and
should)
 be.
  • 37. @Test public void reportsTotalSalesOfOrderedProducts() { Order order1 = anOrder() .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1) .withCustomersReference(1234) .build(); requestSender.send(order1); progressMonitor.waitForCompletion(order1); Order order2 = anOrder() .withLine("Deerstalker Hat", 1) .withCustomersReference(5678) .build(); requestSender.send(order2); progressMonitor.waitForCompletion(order2); TotalSalesReport report = gui.openSalesReport(); report.checkDisplayedTotalSalesFor("Deerstalker Hat", is(equalTo(2) )); report.checkDisplayedTotalSalesFor("Tweed Cape", is(equalTo(1))); }
  • 38. @Test public void reportsTotalSalesOfOrderedProducts() { submitOrderFor("Deerstalker Hat", "Tweed Cape"); submitOrderFor("Deerstalker Hat"); TotalSalesReport report = gui.openSalesReport(); report.checkDisplayedTotalSalesFor("Deerstalker Hat", is(equalTo(2) )); report.checkDisplayedTotalSalesFor("Tweed Cape", is(equalTo(1))); } void submitOrderFor(String ... products) { OrderBuilder orderBuilder = anOrder() .withCustomersReference(nextCustomerReference()); for (String product : products) { orderBuilder = orderBuilder.withLine(product, 1); } Order order = orderBuilder.build(); requestSender.send(order); progressMonitor.waitForCompletion(order); }
  • 39. This
won’t
scale
well: void submitOrderFor(String ... products) { […] void submitOrderFor(String product, int count, String otherProduct, int otherCount) { […] void submitOrderFor(String product, double discount) { […] void submitOrderFor(String product, String giftVoucherCode) { […]
  • 40. Better,
to
use
the
builder: @Test public void reportsTotalSalesOfOrderedProducts() { sendAndProcess(anOrder() .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1)); sendAndProcess(anOrder() .withLine("Deerstalker Hat", 1)); TotalSalesReport report = gui.openSalesReport(); report.checkDisplayedTotalSalesFor("Deerstalker Hat", is(equalTo(2) )); report.checkDisplayedTotalSalesFor("Tweed Cape", is(equalTo(1))); } void sendAndProcess(OrderBuilder orderDetails) { Order order = orderDetails .withDefaultCustomersReference(nextCustomerReference()) .build(); requestSender.send(order); progressMonitor.waitForCompletion(order); }
  • 41. Better
names
improve
clarity: @Test public void reportsTotalSalesOfOrderedProducts() { havingReceived(anOrder() .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1)); havingReceived(anOrder() .withLine("Deerstalker Hat", 1)); TotalSalesReport report = gui.openSalesReport(); report.displaysTotalSalesFor("Deerstalker Hat", equalTo(2)); report.displaysTotalSalesFor("Tweed Cape", equalTo(1) ); }
  • 42. This
is
how
readable
a
test
should
be: @Test public void takesAmendmentsIntoAccountWhenCalculatingTotalSales() { Customer theCustomer = aCustomer().build(); havingReceived(anOrder().from(theCustomer) .withLine("Deerstalker Hat", 1) .withLine("Tweed Cape", 1)); havingReceived(anOrderAmendment().from(theCustomer) .withLine("Deerstalker Hat", 2)); TotalSalesReport report = user.openSalesReport(); report.containsTotalSalesFor("Deerstalker Hat", equalTo(2)); report.containsTotalSalesFor("Tweed Cape", equalTo(1) ); }

Notas do Editor

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. This.lines = lines (aber &amp;#xFC;bergibts OrderLines); deklarierst aber list&lt;orderline&gt; lines\n\nDas passt nicht!\n\npublic OrderBuilder withOrderLines(List&lt;OrderLine&gt; lines) { this.lines = lines; return this; }\n
  27. This.lines = lines (aber &amp;#xFC;bergibts OrderLines); deklarierst aber list&lt;orderline&gt; lines\n\nDas passt nicht!\n\npublic OrderBuilder withOrderLines(List&lt;OrderLine&gt; lines) { this.lines = lines; return this; }\n
  28. This.lines = lines (aber &amp;#xFC;bergibts OrderLines); deklarierst aber list&lt;orderline&gt; lines\n\nDas passt nicht!\n\npublic OrderBuilder withOrderLines(List&lt;OrderLine&gt; lines) { this.lines = lines; return this; }\n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n