Before we talk about Builder Pattern, let's take a look at a scene. Let's say we want to book an IPhone 6, 64G, gold, in code, probably.
1234 |
// PFX 是一个前缀,因为直接写 iPhone6 不符合类名大写的习惯,写成 IPhone6 更是怪异 ╮(╯▽╰)╭ PFXiPhone6 *iphone = [[PFXiPhone6 alloc] init]; iphone.storage = 64; iphone.color = [UIColor goldenColor]; |
Or it could be another way
1 |
PFXiPhone6 *iPhone = [[PFXiPhone6 alloc] initWithStorage:64 color:[UIColor goldenColor]]; |
The first way is better extensibility, but you cannot constrain the need to set some property. The second way fixes the problem, but the extensibility is poor.
If there is a new demand, so that customers can choose to sell areas, such as Hong Kong, National Bank, the United States and so on. For the first, it is natural to add a property, but the user is probably completely unaware of the new attribute. For the second type, you can only create a new initialization mode, such as-[initwithstorage:color:place]. What if there are new needs, such as the choice of lettering, and which words are engraved? Or you can choose the type of shell and so on. Neither of these approaches is a good way to handle changes in requirements.
Now let's talk about Builder pattern, which can be easily implemented in a variety of languages, such as JavaScript
12345 |
new PFXiPhone6Builder() .setStorage(64) .setColor( ‘golden‘ ) .setPlace( ‘HK‘ ) .build(); |
When there is a new attribute, add a setProperty. If you miss a property, you can check it in the build.
In the objective-c, such a chain style is not very popular (masonry in this way of writing used more), so, in OC, it will probably be the case.
12345 |
PFXiPhone6Builder *builder = [[PFXiPhone6Builder alloc] init]; builder.storage = 64; builder.color = [UIColor goldenColor]; builder.place = @ "HK" ; PFXiPhone6 *iphone = [builder build]; |
If there are any attributes missing, check them at build. This solves the problem of inconvenient expansion, which can be known when new attributes are available.
But it still seems elegant, this builder is just a temporary tool, and throw it out, since then, is it possible to fit the OC tradition and let the builder"appear temporarily? And look at the code below
12345 |
PFXiPhone6 *iPhone6 = [PFXiPhone6 createWithBuilder:^(PFXiPhone6Builder *builder){ builder.storage = 64; builder.color = [UIColor goldenColor]; builder.place = @ "HK" ; }]; |
Does it look much more comfortable? Builder works only within the block scope and does not affect the current context variables. The code for this +[createwithbuilder:] is as follows
123456 |
+ (instancetype)createWithBuilder:(BuilderBlock)block { NSParameterAssert(block); PFXiPhone6Builder *builder = [[PFXiPhone6Builder alloc] init]; block(builder); return [builder build]; } |
There are two implementations of the build method here, the first of which
1234567891011121314 |
// PFXiPhone6Builder
- (PFXiPhone6 *)build
{
return
[[PFXiPhone6 alloc] initwithBuilder:self];
}
// PFXiPhone6
- (instancetype)initwithBuilder:(PFXiPhone6Builder *)builder
{
if
(self = [
super
init]) {
_storage = builder.storage;
_color = builder.color;
_place = builder.place;
}
}
|
The other is to combine two processes into a single process
1234567891011 |
// PFXiPhone6Builder
- (PFXiPhone6 *)build
{
// 可以在这里对 property 做检查
NSAssert(self.place, @
"发行区别忘了填哦"
);
PFXiPhone6 *iphone6 = [[PFXiPhone6 alloc] init];
iPhone6.storage = self.storage;
iPhone6.color = self.color;
iPhone6.place = self.place;
return
iPhone6;
}
|
The difference between the two methods is the processing of the parameters, the previous one is processed in the target Class, and the latter is handled in the Builder.
There are similar uses in Facebook pop, such as
123456 |
< Code class= "JS Plain" >popanimatableproperty *animatableproperty = [popanimatableproperty propertywithname:@ initializer:^ ( Popmutableanimatableproperty *prop) { &NBSP;&NBSP;&NBSP;&NBSP; Code class= "JS Plain" >prop.writeblock = ^ (id obj, const cgfloat values[]) { &NBSP;&NBSP;&NBSP;&NBSP; }; &NBSP;&NBSP;&NBSP;&NBSP; prop.readblock = ^ (id obj, cgfloat values[]) { Code class= "JS spaces" >&NBSP;&NBSP;&NBSP;&NBSP; }; |
The initializer here is actually builder.
I am writing the basic framework of Mushroom street, also useful to several places, I think it is quite convenient, especially for users. For example, a collectionview that can be scrolled horizontally or vertically containing clickable Items.
1234567891011121314 |
self.collectionView = [MGJFlowCollectionView collectionViewWithBuilder:^(MGJFlowCollectionViewBuilder *builder) {
builder.scrollDirection = UICollectionViewScrollDirectionHorizontal;
builder.minimumInteritemSpacing = 10;
builder.minimumLineSpacing = 10;
builder.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
CGSize itemSize = CGSizeMake(81, 100);
builder.itemSize = itemSize;
builder.dataSource = @[@1,@2,@3,@4,@5,@6, @7,@8, @9, @10];
builder.cellBuilder = ^UIView *(NSNumber *number){
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, itemSize.width, itemSize.height)];
view.backgroundColor = [UIColor mgj_random];
return
view;
};
}];
|
This makes it possible to create a horizontal or vertical collectionview with a simple configuration.
One of the benefits of using Builder Pattern is the ability to unify fragmented configurations. For example, to create a collectionview, we need to set layout, but also to set some of the layout of the properties, but also set datasource/delegate and so on, can now be set in one place, the readability will be better.
So if you're going to need more than one parameter, or even a parameter that contains a variety of parameters, consider the Builder Pattern.
The use of Builder Pattern in Objective-c