Upload
kuba-marchwicki
View
782
Download
3
Embed Size (px)
DESCRIPTION
This presentation is only in Polish. Slajdy z prezentacji grupy JUG, 25 listopada 2010
Citation preview
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