Meeting 6
Aus JSUG
When: Monday, November 3rd, 2008 - 19:00
Where: Freihaus HS4
Inhaltsverzeichnis |
[Bearbeiten] Bazaar
Download the slides (pdf, 68KB).
You can also view the slides online.
[Bearbeiten] Contents
Bazaar was introduced by Florian Motlik, which is an opensource, distributed verion control system completely written in python.
[Bearbeiten] Core Concepts
First of all, you have to be aware of some necessary terms and their meaning:
- Revision: A snapshot of the files you are working with.
- Working tree: The directory containing your version controlled files and subdirectories.
- Branch: An ordered set of revisions that describe the history of a set of files.
- Repository: A store of revisions.
See: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html#core-concepts
[Bearbeiten] Distributed vs Centralized
The big difference (as compared to traditionally VCS like CVS and SVN) is that bazaar manages sources in a distributed way. Instead of a central server with a single repository where each developer commits his changes, in the bazaar world every developer has its own repository and instead of commits there are only merges.
[Bearbeiten] Workflows
With bazaar, you can work in many different workflows, depending on your environment: solo, partner, central (with local commits) or decentral (shared mainline, human or automated gatekeeper). Depending on that workflows, you have to execute different commands.
[Bearbeiten] Solo & Partner
Initialize the repository:
$ bzr init-repo # create meta repository (for performance reasons) $ bzr init # transform directory into repository
In solo mode:
$ cd my-stuff $ bzr init $ bzr add $ bzr commit -m "Initial import"
In partner mode:
UserA$ bzr init ... some code changes on UserA's machine ... UserA$ bzr commit UserB$ bzr branch http://server.com/UserA ... some code changes on UserB's machine ... UserB$ bzr commit UserA$ merge http://server.com/UserB ... some code changes on UserA's machine ... UserA$ bzr commit UserB$ bzr merge http://server.com/UserA
[Bearbeiten] Central
Necessary steps are similar to CVS/SVN, where the branch is bound to the server and a network connection is required.
$ bzr checkout http://server.com/repo ... some code changes on local machine ... $ bzr commit $ bzr update
[Bearbeiten] Decentral
In this case you mirror the main branch which is not supposed to be used for development.
$ bzr branch http://server.com/trunk $ bzr branch trunk feature1 ... some code changes related to feature1 ... $ bzr branch trunk fix1 ... some code changes related to fix1 ... $ cd trunk $ bzr merge ../fix1 $ bzr commit # locally $ bzr update # to update the mirrored branch
[Bearbeiten] Links
- http://bazaar-vcs.org/ ... Official website
- http://doc.bazaar-vcs.org/bzr.dev/ ... User documentation
- https://launchpad.net/ ... Hosting service like sourceforge using bazaar
[Bearbeiten] Maven
[Bearbeiten] Fluent Interfaces
Unfortunately there are no slides available for the presentation from Axel Gross but you can have a look at Martin Fowler's article about fluent interfaces who coined the term.
To be short, it's kind of an internal DSL with the aim to provide a readable API.
The following example shows the use of JMock:
mock.expects(once()).method("m").with(or(stringContains("hello"), stringContains("howdy")));
In a preceding presentation about db4o we had similar code for querying a OO database:
query.descend("matrNr").constrain(500000).greater().equal();
[Bearbeiten] Simple Example
Personally, I (Christoph Pickl) like to use that technique to construct complex objects which should be on the one side immutable but on the other easy to create (values for creation are sometimes scattered throughout many different classes). Imagine you have a Person type with its only attributes name:String and age:int which are both marked final.
public class Person { // final attributes private final String name; private final int age; // constructor is package-private, so only PersonBuilder can access it Person(final String name, final int age) { this.name = name; this.age = age; } // simple getter methods public String getName() { return this.name; } public int getAge() { return this.age; } }
A PersonBuilder now provides the single possibility to create Persons. The interesting thing here is, that it forces the caller to invoke methods for each attribute of Person, and returns itself but with another "face" (builder interface). Only if the last attribute was set the create() method is accessible which finally returns a new instance.
Guard clauses (actually used to avoid nested conditions) help to ensure that the instance will only store valid values. This gives the advantage of defining a valid state outside of the entity itself so it can be reused in a complete different environment with complete different limitations (name can be null, etc).
public class PersonBuilder implements IPersonBuilder1, IPersonBuilder2, IPersonBuilderFinisher { // (non-final) attributes private String name; private int age; // constructor private PersonBuilder() { // constructor not visible; use static factory method build() } public static IPersonBuilder1 build() { return new PersonBuilder(); } // fluent interface implementations public IPersonBuilder2 name(final String name) { if(name == null) { // guard clause throw new IllegalArgumentException("name == null"); } this.name = name; return this; } public IPersonBuilderFinisher age(final int age) { if(age < 0 || age > 200) { // guard clause throw new IllegalArgumentException("age not within range 0-200: " + age); } this.age = age; return this; } public Person newInstance() { return new Person(this.name, this.age); } }
Two obvious disadvantages come to my mind: The overhead of creating an interface for each method plus one for the creation and you have to invoke the methods in exactly the way they are defined via the fluent interfaces. You can't change the order, and you can't skip any attribute.
public interface IPersonBuilder1 { IPersonBuilder2 name(String name); } public interface IPersonBuilder2 { IPersonBuilderFinisher age(int age ); } public interface IPersonBuilderFinisher { Person newInstance(); }
Finally you can use the builder to create Person instances via a very readable API. The second Person instance is created in a distributed manner, which would not be that easy if you would have to create the instance in the old-fashioned way because of final attributes you have to pass to the constructor.
public class App { public static void main(final String[] args) { // short version: final Person p1 = PersonBuilder.build().name("Christoph Pickl").age(23).newInstance(); // or use different builders and pass them around final IPersonBuilder2 builder2 = PersonBuilder.build().name("Christopherus Pickl"); final Person p2 = App.computeAge(builder2).newInstance(); } private static IPersonBuilderFinisher computeAge(final IPersonBuilder2 builder2) { final int age = 42; // some complicated computation return builder2.age(age); } }
