[PL] O klasycznej, programistycznej elegancji

Post on 26-May-2015

782 views 3 download

description

This presentation is only in Polish. Slajdy z prezentacji grupy JUG, 25 listopada 2010

Transcript of [PL] O klasycznej, programistycznej elegancji

O klasycznej programistycznej elegancji

http://my.opera.com/rato53/albums/

Jakub MarchwickiTechnical ConsultantJava, Lua, PHPi tak od 9lat

marchwicki.pl/blog@kubamarchwicki

Kanon piękna?

© vitra

Kanon piękna?

„Any fool can write code that a

computer can understand. Good

programmers write code that

humans can understand“

Refactoring: Improving the Design of Existing Code, 1999

Martin Fowler jeszcze raz

Joshua Bloch

Effective Java

I sure wish I had this book ten

years agoJames Gosling

Kent Beck

„Many people don’t realize how readable code can be and how valuable that readability is. Kent has taught me so...“ Martin Fowler

Implementation patterns

CommunicationSimplicityFlexibility

A wszystko dlatego żenajwiększym kosztem

oprogramowania będzie i tak jego

utrzymanie

Kryteria dobrego kodu

© http://www.agileadvisor.com/

private static boolean isOlderExtremeVersion(String d1, String d2) {String[] dateOneA = dateOne.split("-");String[] dateTwoA = dateTwo.split("-");

if ((Integer.valueOf(dateOneA[0]).intValue() > Integer.valueOf(dateTwoA[0]).intValue())

|| (Integer.valueOf(dateOneA[0]).intValue() == Integer.valueOf(dateTwoA[0]).intValue() && Integer.valueOf(dateOneA[1]).intValue() >

Integer.valueOf(dateTwoA[1]).intValue()) || (Integer.valueOf(dateOneA[0]).intValue() ==

Integer.valueOf(dateTwoA[0]).intValue() && Integer.valueOf(dateOneA[1]).intValue() ==

Integer.valueOf(dateTwoA[1]).intValue() && Integer.valueOf(dateOneA[2]).intValue() > Integer.valueOf(dateTwoA[2]).intValue())) {

return false;}

return true;}

Nie będzie Eclipsa...

public class Person {private String firstName;private String lastName;private long birthDate;

}

public class Student extends Person {private int year;

}

public class Professor extends Person {private String[] specjalities;

}

public class Lecture {private String title;private Professor professor;private Student[] students;

}

Wyobraźmy sobie model...

public boolean equals(Object o) {if (!(o instanceof Person))

return false;final Person p = (Person) o;return firstName.equals(p.firstName)

&& lastName.equals(p.lastName)&& birthDate == p.birthDate;

}

public int hashcode() {int result = 17;result = 37*result + firstName.hashCode();result = 37*result + lastName.hashCode();result = 37*result + (int)(birthDate ^ birthDate >>> 32);return result;

}

A teraz piszemy...

public int hashCode() {final int prime = 31;int result = 1;result = prime * result + Arrays.hashCode(specjalities);return result;

}

public boolean equals(Object obj) {if (this == obj) {

return true;}if (!super.equals(obj)) {

return false;}if (!(obj instanceof Professor)) {

return false;}Professor other = (Professor) obj;if (!Arrays.equals(specjalities, other.specjalities)) {

return false;}return true;

}

lub generujemy...

public String toString() {return "Professor [specjalities=" +

Arrays.toString(specjalities) + "]";}

to samo tyczy się toString()'a

public boolean equals(Object o) {if (o == null) { return false; }if (o == this) { return true; }if (o.getClass() != getClass()) { return false; }final Student s = (Student) o;return new EqualsBuilder()

.appendSuper(super.equals(o)).append(year, s.year)

.isEquals();}

public int hashcode() {return new HashCodeBuilder(17, 37)

.appendSuper(super.hashcode()).append(year)

.toHashCode();}

public String toString() {return new ToStringBuilder(this)

.appendSuper(super.toString()).append("year", year)

.toString();}

ale przy setce encji się nie chcieć

public boolean equals(Object o) {if (o instanceof Lecture) {

Lecture l = (Lecture) o;return Objects.equal(professor, l.professor)

&& Objects.equal(students, l.students)&& Objects.equal(title, l.title);

}

return false;}

public int hashcode() {return Objects.hashCode(title, professor, students);

}

public String toString() {return Objects.toStringHelper(this)

.add("professor", professor)

.add("students", students)

.toString();}

albo mniej fundamentalnie

public void addLectureWithCommons(Lecture lecture, Venue venue,int dayOfTheWeek, int hour) {

Validate.isTrue(dayOfTheWeek > 0 && dayOfTheWeek <= 7,"There are only 7 days of the week");

Validate.isTrue(hour >= 0 && hour < 23, "Day has only 24hours");Validate.notNull(lecture != null, "Lecture cannot be null");Validate.notNull(venue != null, "Venue cannot be null");// reminder omitted

}public void addLectureWithGuava(Lecture lecture, Venue venue,

int dayOfTheWeek, int hour) {Preconditions.checkArgument(dayOfTheWeek > 0 && dayOfTheWeek <= 7,

"There are only 7 days of the week");Preconditions.checkArgument(hour >= 0 && hour < 23,

"Day has only 24hours");Preconditions.checkArgument(lecture != null, "Lecture cannot be null");Lecture localLecture = Preconditions.checkNotNull(lecture);Venue localVenue = Preconditions.checkNotNull(venue);// reminder omitted

}

defensive programming

String[] csv;

for (String line : csv) {String[] elements = line.split(",");// reminder omitted

}

for (String line : csv) {String[] r = StringUtils.split(line, ",");r = StringUtils.stripAll(r);

}

for (String line : csv) {Splitter.on(",")

.trimResults()

.omitEmptyStrings()

.split(line);}

a później zawsze trafimy na String'a

public interface LectureService {public Student[] getStudentsByYear(int year);public Student[] getStudentsOlderThan(int age);public Student[] getStudentsByBirthDate(Date date);

}

i kilka operacji 'biznesowych'

public Student[] getStudentsByYear(int year) {final List<Student> students = new ArrayList<Student>();for (Student s : lecture.getStudents()) {

if (s.getYear() == year) {students.add(s);

}}

return students.toArray(new Student[] {});}

które sprowadzają się...

public Student[] getStudentsByBirthDate(Date date) {final List<Student> students = new ArrayList<Student>();for (Student s : lecture.getStudents()) {

Calendar studentBirth = Calendar.getInstance();studentBirth.setTimeInMillis(s.getBirthDate());

Calendar desiredDate = Calendar.getInstance();desiredDate.setTime(date);

if (studentBirth.get(Calendar.YEAR) == desiredDate.get(Calendar.YEAR)

&& studentBirth.get(Calendar.MONTH) ==desiredDate.get(Calendar.MONTH)

&& studentBirth.get(Calendar.DATE) ==desiredDate.get(Calendar.DATE)) {

students.add(s);}

}

return students.toArray(new Student[] {});}

... do niemal ...

public Student[] getStudentsOlderThan(int age) {final List<Student> students = new ArrayList<Student>();for (Student s : lecture.getStudents()) {

Calendar c = Calendar.getInstance();c.setTimeInMillis(s.getBirthDate());c.add(Calendar.YEAR, age);

Calendar now = Calendar.getInstance();now.setTime(new Date());if (c.before(now)) {

students.add(s);}

}

return students.toArray(new Student[] {});}

... tego samego.

public Student[] getStudentsOlderThan(int age) {final List<Student> students = new ArrayList<Student>();for (Student s : lecture.getStudents()) {

DateMidnight dt = new DateMidnight().withMillis(s.getBirthDate()).plusYears(age);

if (dt.isBeforeNow()) {students.add(s);

}}

return students.toArray(new Student[] {});}

i abstahując od użycia JodaTime

private Student[] getStudents(Predicate predicate) {final List<Student> students = new ArrayList<Student>();for (Student s : lecture.getStudents()) {

if (predicate.evaluate(s)) {students.add(s);

}}

return students.toArray(new Student[] {});}

Don't Repeat Yourself

Predicate predicate = new Predicate() {@Overridepublic boolean evaluate(Object arg0) {

if (arg0 instanceof Student) {Student s = (Student) arg0;DateMidnight dt = new DateMidnight()

.withMillis(s.getBirthDate())

.plusYears(age);if (dt.isBeforeNow()) {

return true;}

}

return false;}

};

No a ten predykat?

Predicate<Student> predicate = new Predicate<Student>() {@Overridepublic boolean apply(Student input) {

DateMidnight dt = new DateMidnight().withMillis(input.getBirthDate()).plusYears(age);

if (dt.isBeforeNow()) {return true;

}

return false;}

};

No a ten predykat? (2)

private Student[] getStudents(Predicate predicate) {final List<Student> students = new ArrayList<Student>();for (Student s : lecture.getStudents()) {

if (predicate.evaluate(s)) {students.add(s);

}}

return students.toArray(new Student[] {});}

i jeszcze na koniec, zamiast...

protected Student[] getStudents(Predicate<Student> predicate) {Iterable<Student> students = Iterables

.filter(Arrays.asList(lecture.getStudents()), predicate);return Iterables.toArray(students, Student.class);

}

... może być w ogóle ślicznie

Student[] students = service.promoteStudents(2);

Predicate<Student> predicate = new Predicate<Student>() {public boolean apply(Student input) {

if (input.getYear() == year) {return true;

}return false;

}};

Function<Student,Student> function = new Function<Student,Student>(){public Student apply(Student input) {

input.setYear(year + 1);return input;

}};

Iterables.filter();Iterables.transform();

Jak już jest prawie funkcyjnie...

Guava to nie tylko <generics>

© Kevin Bourrillion, Google, Inc.

private Map<Student, List<Lecture>> classes = new HashMap<Student, List<Lecture>>();

// ...

for (Lecture lecture : lectures) {for (Student student : lecture.getStudents()) {

if (service.classes.containsKey(student)) {service.classes.get(student).add(lecture);

} else {List<Lecture> l = new ArrayList<Lecture>();l.add(lecture);service.classes.put(student, l);

}}

}

kiedyś było tak...

private Multimap<Student, Lecture> classes =ArrayListMultimap.create();

// ...

for (Lecture lecture : lectures) {for (Student student : lecture.getStudents()) {

service.classes.put(student, lecture);}

}Map<Student, Collection<Lecture>> map = classes.asMap();Collection<Lecture> lectures = classes.values();Set<Student> students = classes.keySet();for (Map.Entry<Student, Lecture> entry : classes.entries()) {}

... a teraz witamy Multimap i Multiset

Map<Student, Collection<Lecture>> map = classes.asMap();"JM" => {"Fizyka", "Chemia", "Matematyka"}, "TD" => {"Chemia", Biologia"}

Collection<Lecture> lectures = classes.values();{"Fizyka", "Matematyka", "Chemia", "Biologia"}

Set<Student> students = classes.keySet();{"JM", "TD"}

for (Map.Entry<Student, Lecture> entry : classes.entries()) {}{"JM"=>"Fizyka", "JM"=>"Matematyka", "JM"=>"Chemia", "TD"=>"Biologia", ...}

Collection<Lecture> lectures = classes.get("JM");{"Fizyka", "Chemia", "Matematyka"}

Multimap i Multiset (2)

ImmutableSet<Student> students = ImmutableSet.copyOf(service.getStudentsByYear(2));

Set<Student> students = Collections.unmodifiableSet(

new LinkedHashSet<Student>(Arrays.asList(service.getStudentsByYear(2))));

Immutability

new Person("Ridge", "Forrster");Person person = new Person.Builder()

.withName(“Ridge”)

.withSurname(“Forrester”)

.build();

Immutability with builders

http://www.marchwicki.pl/blog/2010/11/building-a-pojo-in-an-elegant-way/

Dziękuje

http://marchwicki.pl/blog

@kubamarchwicki

http://www.delicious.com/kuba.marchwicki/beautifulcode

http://www.assembla.com/code/km_jug/subversion/nodes