IOS development: Eight attempts to bring you into lean Programming

Source: Internet
Author: User

IOS development: Eight attempts to bring you into lean Programming

Opening

Today, we will start with a small feature and implement it without thinking about it.

Product Repository: Filtering Operation

Code start

There is a product library that we want to filter.

The first requirement is not complex.

Requirement 1: search for all products in red in the repository

First Attempt: Hard Code

We first implement it in the simplest way, hard Coding

-(NSArray *) findAllRedProducts :( NSArray *) products

{

NSMutableArray * list = [@ [] mutableCopy];

For (Product * product in products ){

If (product. color = RED ){

[List addObject: product];

}

}

Return list;

}

If the world is always static, such an implementation is understandable, but this is often not the case in the world.

Then the second requirement came.

Requirement 2: search for all products in green color in the warehouse

Second Attempt: Parameterizing

Copy-Paste is the easiest problem for most programmers. Therefore, a large number of repeated codes are introduced.

-(NSArray *) findAllGreenProducts :( NSArray *) products

{

NSMutableArray * list = [@ [] mutableCopy];

For (Product * product in products ){

If (product. color = GREEN ){

[List addObject: product];

}

}

Return list;

}

To eliminate hard coding and obtain reusable code, simple parametric design can be introduced.

-(NSArray *) findProducts :( NSArray *) products byColor :( ProductColor) color

{

NSMutableArray * list = [@ [] mutableCopy];

For (Product * product in products ){

If (product. color = color ){

[List addObject: product];

}

}

Return list;

}

Finally, we can rest assured that at this time, how can our product manager make you comfortable? demand 3 is coming again.

Requirement 3: search for all products whose weight is less than 10

Third Attempt: Parameterizing with Every Attribute You Can Think

Most programmers still use Copy-Paste to solve this problem and reject Copy-Paste's bad habits. The most effective feedback is to invalidate this shortcut key, this reminds you to make better designs every time you try Copy-Paste.

-(NSArray *) findProducts :( NSArray *) products byWeith :( float) weight

{

NSMutableArray * list = [@ [] mutableCopy];

For (Product * product in products ){

If (product. weight <weight ){

[List addObject: product];

}

}

Return list;

}

In order to eliminate repeated code, such problems cannot be solved perfectly through simple parameterization. On the contrary, excessive complexity and occasional costs will be introduced.

-(NSArray *) findProducts :( NSArray *) products byColor :( ProductColor) color byWeith :( float) weight type :( int) type

{

NSMutableArray * list = [@ [] mutableCopy];

For (Product * product in products ){

If (type = 1) & product. color = color ){

[List addObject: product];

Continue;

}

Else if (type = 2) & (product. weight <weight ))

{

[List addObject: product];

Continue;

}

}

Return list;

}

In daily work, this implementation method is very common. As the list of function parameters increases with the demand, the function logic assumes more and more responsibilities, and the logic becomes increasingly difficult to control.

The design that uses parameter configuration to cope with changes often fails.

It is easy to cause complicated logic control and lead to additional occasional complexity.

Forth Attempt: Illegal acting over Criteria

Therefore, it is necessary to abstract the Traversal Algorithms and search standards so that they can change independently without affecting each other.

@ Interface ProductSpec: NSObject

-(BOOL) satisfy :( Product *) product;

@ End

At this moment, the filter algorithm logic is closed. Of course, the function name must be renamed to make its algorithm implementation more universal.

-(NSArray *) findProducts :( NSArray *) products bySpec :( ProductSpec *) spec

{

NSMutableArray * list = [@ [] mutableCopy];

For (Product * product in products ){

If ([spec satisfy: product]) {

[List addObject: product];

}

}

Return list;

}

It encapsulates various changes through reusable classes, so that the changing factors can be controlled within the minimum range.

@ Interface ColorSpec ()

@ Property (nonatomic, assign) ProductColor;

@ End

@ Implementation ColorSpec

+ (Instancetype) specWithColor :( ProductColor) color

{

ColorSpec * spec = [[ColorSpec alloc] init];

Spec. color = color;

Return spec;

}

-(BOOL) satisfy :( Product *) product

{

Return product. color = RED;

}

@ End

@ Interface BelowWeightSpec ()

@ Property (nonatomic, assign) float limit;

@ End

@ Implementation BelowWeightSpec

+ (Instancetype) specWithBelowWeight :( float) limit

{

BelowWeightSpec * spec = [[BelowWeightSpec alloc] init];

Spec. limit = limit;

Return spec;

}

-(BOOL) satisfy :( Product *) product

{

Return (product. weight <_ limit );

}

@ End

User interfaces are much simpler and expressive.

[Self findProducts: _ products bySpec: [ColorSpec specWithColor: RED];

This is a classic OO design. If you are familiar with the design model, you are used to it. Design Patterns are good but often abused. Therefore, it is natural to introduce the design pattern in order to get a simpler design.

Communicate with masters and ask why the design model is introduced here. The answer is intuition. Forget all the design patterns, whether it is a pattern. If the design is simple, it is a pattern.

There is also an obvious bad taste. Both ColorSpec and BelowWeightSpec must inherit ProductSpec, define a constructor and a private field, and override the satisfy method, which is filled with repeated structures.

Do you think the current writing method is enough? Don't worry. Let's take a look at the next requirement.

Requirement 4: search for all products whose colors are red and whose weight is less than 10

Firth Attempt: Composite Criteria

According to the existing code structure, it is often easy to design the implementation similar to ColorAndBelowWeightSpec.

@ Interface ColorAndBelowWeigthSpec ()

@ Property (nonatomic, assign) ProductColor;

@ Property (nonatomic, assign) float limit;

@ End

@ Implementation ColorAndBelowWeigthSpec

+ (Instancetype) specWithColor :( ProductColor) color beloWeigth :( float) limit

{

ColorAndBelowWeigthSpec * spec = [[ColorAndBelowWeigthSpec alloc] init];

Spec. color = color;

Spec. limit = limit;

Return spec;

}

-(BOOL) satisfy :( Product *) product

{

Return product. color = _ color | (product. weight <_ limit );

}

@ End

There are two obvious bad tastes:

The name of include and is often a signal light that violates a single responsibility

The implementation of ColorAndBelowWeightSpec is significantly different from that of ColorSpec and BelowWeightSpec.

At this moment, we need to find more essential abstractions to express design. and/or/not semantics can perfectly solve such problems.

Composite Spec: AndSpec, OrSpec, NotSpec

Atomic Spec: ColorSpec, BeblowWeightSpec

@ Interface AndSpec ()

@ Property (nonatomic, strong) NSArray * specs;

@ End

@ Implementation AndSpec

+ (Instancetype) spec :( ProductSpec *) spec,... NS_REQUIRES_NIL_TERMINATION

{

Va_list args;

Va_start (args, spec );

NSMutableArray * mArray = [@ [spec] mutableCopy];

For (;;)

{

Id tempSpec = va_arg (args, id );

If (tempSpec = nil)

Break;

[MArray addObject: tempSpec];

}

Va_end (args );

AndSpec * andSpec = [[AndSpec alloc] init];

AndSpec. specs = [mArray copy];

Return andSpec;

}

-(BOOL) satisfy :( Product *) product

{

For (ProductSpec * spec in _ specs ){

If (! [Spec satisfy: product]) {

Return NO;

}

}

Return YES;

}

@ End

@ Interface OrSpec ()

@ Property (nonatomic, strong) NSArray * specs;

@ End

@ Implementation OrSpec

+ (Instancetype) spec :( ProductSpec *) spec,... NS_REQUIRES_NIL_TERMINATION

{

Va_list args;

Va_start (args, spec );

NSMutableArray * mArray = [@ [spec] mutableCopy];

For (;;)

{

Id tempSpec = va_arg (args, id );

If (tempSpec = nil)

Break;

[MArray addObject: tempSpec];

}

Va_end (args );

OrSpec * orSpec = [[OrSpec alloc] init];

OrSpec. specs = [mArray copy];

Return orSpec;

}

-(BOOL) satisfy :( Product *) product

{

For (ProductSpec * spec in _ specs ){

If ([spec satisfy: product]) {

Return YES;

}

}

Return NO;

}

@ End

@ Interface NotSpec ()

@ Property (nonatomic, strong) ProductSpec * spec;

@ End

@ Implementation NotSpec

+ (Instancetype) spec :( ProductSpec *) spec

{

NotSpec * notSpec = [[NotSpec alloc] init];

NotSpec. spec = spec;

Return notSpec;

}

-(BOOL) satisfy :( Product *) product

{

If (! [_ Spec satisfy: product]) {

Return YES;

}

Return NO;

}

@ End

You can use the AndSpec combination of ColorSpec and BelowWeightSpec to achieve your needs. It is simple and elegant, and is full of expressiveness.

[Self findProducts: _ products bySpec: [AndSpec spec: [ColorSpec specWithColor: RED], [BelowWeightSpec specWithBelowWeight: 10], nil];

But there are two serious bad questions about this design:

There are obvious code duplication between AndSpec and OrSpec. The first intuition of OO design is to eliminate duplication by extracting base classes.

@ Interface CombinableSpec ()

@ Property (nonatomic, strong) NSArray * specs;

@ End

@ Implementation CombinableSpec

+ (Instancetype) spec :( CombinableSpec *) spec,... NS_REQUIRES_NIL_TERMINATION

{

Va_list args;

Va_start (args, spec );

NSMutableArray * mArray = [@ [spec] mutableCopy];

For (;;)

{

Id tempSpec = va_arg (args, id );

If (tempSpec = nil)

Break;

[MArray addObject: tempSpec];

}

Va_end (args );

CombinableSpec * combinableSpec = [[CombinableSpec alloc] init];

CombinableSpec. specs = [mArray copy];

Return combinableSpec;

}

-(BOOL) satisfy :( Product *) product

{

For (ProductSpec * spec in _ specs ){

If ([spec satisfy: product] ==_ shortcut ){

Return _ shortcut;

}

}

Return! _ Shortcut;

}

@ End

@ Implementation AndSpec

-(Instancetype) init

{

Self = [super init];

If (self ){

Self. shortcut = NO;

}

Return self;

}

@ End

@ Implementation OrSpec

-(Instancetype) init

{

Self = [super init];

If (self ){

Self. shortcut = YES;

}

Return self;

}

@ End

A bunch of initialization methods are dazzling

[Self findProducts: _ products bySpec: [NotSpec spec: [AndSpec spec: [ColorSpec specWithColor: RED], [BelowWeightSpec specWithBelowWeight: 10], nil];

Sixth Attempt: Using DSL

DSL can be introduced to improve program readability and make the code more expressive.

Let's first add some DSL:

Static ProductSpec * COLOR (ProductColor color)

{

Return [ColorSpec specWithColor: RED];

}

Static ProductSpec * BELOWWEIGHT (float limit)

{

Return [BelowWeightSpec specWithBelowWeight: limit];

}

Static ProductSpec * AND (ProductSpec * spec1, ProductSpec * spec2)

{

Return [AndSpec spec: spec1, spec2, nil];

}

Static ProductSpec * OR (ProductSpec * spec1, ProductSpec * spec2)

{

Return [OrSpec spec: spec1, spec2, nil];

}

Static ProductSpec * NOT (ProductSpec * spec)

{

Return [NotSpec spec: spec];

}

In this way, our code is like this.

[Self findProducts: _ products bySpec: NOT (AND (COLOR (RED), BELOWWEIGHT (10)];

Seventh Attempt: Using a Lambda Expression

You can use Block to improve the design and enhance the expressiveness.

-(NSArray *) findProducts :( NSArray *) products byBlock :( BOOL (^) () block

{

NSMutableArray * list = [@ [] mutableCopy];

For (Product * product in products ){

If (block (product )){

[List addObject: product];

}

}

Return list;

}

The code looks like this now

[Self findProducts: _ products byBlock: ^ BOOL (id p) {return [p color] = RED;}];

Construct DSL and reuse these blocks

ProductSpecBlock color (ProductColor color)

{

Return ^ BOOL (id p) {return [p color] = color ;};

}

ProductSpecBlock weightBelow (float limit)

{

Return ^ BOOL (id p) {return [p weight] <limit ;};

}

-(Void) test7_2

{

[Self findProducts: _ products byBlock: color (RED)];

}

Eighth attempt: Using NSPredicate

You can also use the standard library

[Self. products filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @ "weight> 10"];

End

Today's coding ends. This article is written by Horance and I will use OC to implement it again. if we are not iOS Developer, there are other attempt, such as generic.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.