Friday, December 11, 2015

[UML Class relationships] Phân biệt quan hệ Aggregation và Composition trong Objective C và Swift

Aggregation

ClassA có dùng lớp và giữa ClassB nhưng không cho lớp khác truy xuất ClassB thông qua ClassA.  Trong Objective C ta dùng thuộc tính là weak để thể hiện mối quan hệ này, code ví dụ như sau:
#import <Foundation/Foundation.h>
#import "ObjCAggregation_ClassB.h"
@interface ObjCAggregation_ClassA : NSObject
- (instancetype)initWithObject:(ObjCAggregation_ClassB*)inputedObject;
- (BOOL)doAction;
@end
#import "ObjCAggregation_ClassA.h"
@interface ObjCAggregation_ClassA () {
__weak ObjCAggregation_ClassB* refInstance;
}
@end
@implementation ObjCAggregation_ClassA
@synthesize classB;
- (instancetype)initWithObject:(ObjCAggregation_ClassB*)inputedObject {
if (self) {
refInstance = inputedObject;
}
return self;
}
- (void)dealloc {
NSLog(@"[Aggregation] dealloc");
}
- (BOOL)doAction {
NSLog(@"[Aggregation] Action A");
if (refInstance) {
[refInstance doAction];
return YES;
}
return NO;
}
@end
Ngoài ra ta còn có sơ đồ Aggregation - Navigability:
Theo sơ đồ này thì ta dùng property để lớp khác có thể truy xuất ClassB thông qua ClassA.
Code ví dụ trong Objective C như:
#import <Foundation/Foundation.h>
#import "ObjCAggregation_ClassB.h"
@interface ObjCAggregation_ClassA : NSObject
@property (nonatomic, weak) ObjCAggregation_ClassB* classB;
- (instancetype)initWithObject:(ObjCAggregation_ClassB*)inputedObject;
- (BOOL)doAction;
@end
#import "ObjCAggregation_ClassA.h"
@implementation ObjCAggregation_ClassA
@synthesize classB;
- (instancetype)initWithObject:(ObjCAggregation_ClassB*)inputedObject {
if (self) {
classB = inputedObject;
}
return self;
}
- (void)dealloc {
NSLog(@"[Aggregation] dealloc");
}
- (BOOL)doAction {
NSLog(@"[Aggregation] Action A");
if (classB) {
[classB doAction];
return YES;
}
return NO;
}
@end
Unit Test giả sử ta huỷ đối tượng ClassB thì ClassA sẽ không gọi hàm trong ClassB được, do đó trong sơ đồ này nên dùng if để kiểm tra đối tượng khi truyền vào để tránh bị crash ứng dụng:
/**
* Todo list:
// Aggregation
* - Test aggregation, should return true.
* - Test aggregation, should return false, when release reference object.
* - Test aggregation, should return true.
* - Test aggregation, should return false, when release reference object.
**/
- (void)testAggregation1 {
//GIVEN
ObjCAggregation_ClassB* objB = [[ObjCAggregation_ClassB alloc] init];
ObjCAggregation_ClassA* objA = [[ObjCAggregation_ClassA alloc] initWithObject:objB];
//WHEN
BOOL result = [objA doAction];
//THEN
XCTAssertTrue(result);
}
- (void)testAggregation2 {
//GIVEN
ObjCAggregation_ClassB* objB = [[ObjCAggregation_ClassB alloc] init];
ObjCAggregation_ClassA* objA = [[ObjCAggregation_ClassA alloc] initWithObject:objB];
objB = nil;
//WHEN
BOOL result = [objA doAction];
//THEN
XCTAssertFalse(result);
}
- (void)testAggregation3 {
//GIVEN
ObjCAggregation_ClassB* objB = [[ObjCAggregation_ClassB alloc] init];
ObjCAggregation_ClassA* objA = [[ObjCAggregation_ClassA alloc] init];
objA.classB = objB;
//WHEN
BOOL result = [objA doAction];
//THEN
XCTAssertTrue(result);
}
- (void)testAggregation4 {
//GIVEN
ObjCAggregation_ClassB* objB = [[ObjCAggregation_ClassB alloc] init];
ObjCAggregation_ClassA* objA = [[ObjCAggregation_ClassA alloc] init];
objA.classB = objB;
objB = nil;
//WHEN
BOOL result = [objA doAction];
//THEN
XCTAssertFalse(result);
}
Code ví dụ trong Swift như sau:
class SwiftAggregation_ClassA {
weak var classB:SwiftAggregation_ClassB?
init() {
}
init(refClass: SwiftAggregation_ClassB) {
self.classB = refClass
}
func doAction() -> Bool {
print("Do action A")
if ((classB) != nil) {
classB?.doAction()
return true
}
return false
}
}
class SwiftAggregation_ClassB {
func doAction() {
print("Do action B")
}
}
func testAggregation1() {
//GIVEN
var objB:SwiftAggregation_ClassB! = SwiftAggregation_ClassB()
let objA:SwiftAggregation_ClassA = SwiftAggregation_ClassA(refClass: objB)
//WHEN
let result:Bool = objA.doAction()
//THEN
XCTAssertTrue(result)
}
func testAggregation2() {
//GIVEN
var objB:SwiftAggregation_ClassB! = SwiftAggregation_ClassB()
let objA:SwiftAggregation_ClassA = SwiftAggregation_ClassA(refClass: objB)
objB = nil
//WHEN
let result:Bool = objA.doAction()
//THEN
XCTAssertFalse(result)
}
func testAggregation3() {
//GIVEN
var objB:SwiftAggregation_ClassB! = SwiftAggregation_ClassB()
let objA:SwiftAggregation_ClassA = SwiftAggregation_ClassA()
objA.classB = objB
//WHEN
let result:Bool = objA.doAction()
//THEN
XCTAssertTrue(result)
}
func testAggregation4() {
//GIVEN
var objB:SwiftAggregation_ClassB! = SwiftAggregation_ClassB()
let objA:SwiftAggregation_ClassA = SwiftAggregation_ClassA()
objA.classB = objB
objB = nil
//WHEN
let result:Bool = objA.doAction()
//THEN
XCTAssertFalse(result)
}

Composition

Composition giống như Aggregation nhưng giữ đối tượng theo kiểu strong, thì sẽ không phụ thuộc vào đối tượng truyền vào.
Giống code ở trên nhưng ta thay weak thành strong thì sẽ thể hiện mối quan hệ này, ví dụ như sau:
#import <Foundation/Foundation.h>
#import "ObjCComposition_ClassB.h"
@interface ObjCComposition_ClassA : NSObject
@property (nonatomic, strong) ObjCComposition_ClassB* classB;
- (instancetype)initWithObject:(ObjCComposition_ClassB*)inputedObject;
- (BOOL)doAction;
@end
#import "ObjCComposition_ClassA.h"
@implementation ObjCComposition_ClassA
@synthesize classB;
- (instancetype)initWithObject:(ObjCComposition_ClassB*)inputedObject {
if (self) {
classB = inputedObject;
}
return self;
}
- (void)dealloc {
NSLog(@"[Composition] dealloc");
}
- (BOOL)doAction {
NSLog(@"[Composition] Action A");
if (classB) {
[classB doAction];
return YES;
}
return NO;
}
@end

Cũng viết UnitTest như trên nhưng kết quả khi huỷ object tham chiếu thì đối tượng đó vẫn được giữ lại để thực hiện phương thức:
/**
* Todo list:
// Composition
* - Test composition, should return true.
* - Test composition, should return true, when release reference object.
* - Test composition, should return true.
* - Test composition, should return true, when release reference object.
*/
- (void)testComposition1 {
//GIVEN
ObjCComposition_ClassB* objB = [[ObjCComposition_ClassB alloc] init];
ObjCComposition_ClassA* objA = [[ObjCComposition_ClassA alloc] initWithObject:objB];
//WHEN
BOOL result = [objA doAction];
//THEN
XCTAssertTrue(result);
}
- (void)testComposition2 {
//GIVEN
ObjCComposition_ClassB* objB = [[ObjCComposition_ClassB alloc] init];
ObjCComposition_ClassA* objA = [[ObjCComposition_ClassA alloc] initWithObject:objB];
objB = nil;
//WHEN
BOOL result = [objA doAction];
//THEN
XCTAssertTrue(result);
}
- (void)testComposition3 {
//GIVEN
ObjCComposition_ClassB* objB = [[ObjCComposition_ClassB alloc] init];
ObjCComposition_ClassA* objA = [[ObjCComposition_ClassA alloc] init];
objA.classB = objB;
//WHEN
BOOL result = [objA doAction];
//THEN
XCTAssertTrue(result);
}
- (void)testComposition4 {
//GIVEN
ObjCComposition_ClassB* objB = [[ObjCComposition_ClassB alloc] init];
ObjCComposition_ClassA* objA = [[ObjCComposition_ClassA alloc] init];
objA.classB = objB;
objB = nil;
//WHEN
BOOL result = [objA doAction];
//THEN
XCTAssertTrue(result);
}

Code ví dụ trong Swift như sau:
class SwiftComposition_ClassA {
var classB:SwiftComposition_ClassB? //Default: strong reference
init() {
}
init(refClass: SwiftComposition_ClassB) {
self.classB = refClass
}
func doAction() -> Bool {
print("Do action A")
if ((classB) != nil) {
classB?.doAction()
return true
}
return false
}
}
class SwiftComposition_ClassB {
func doAction() {
print("Do action B")
}
}
func testComposition1() {
//GIVEN
var objB:SwiftComposition_ClassB! = SwiftComposition_ClassB()
let objA:SwiftComposition_ClassA = SwiftComposition_ClassA(refClass: objB)
//WHEN
let result:Bool = objA.doAction()
//THEN
XCTAssertTrue(result)
}
func testComposition2() {
//GIVEN
var objB:SwiftComposition_ClassB! = SwiftComposition_ClassB()
let objA:SwiftComposition_ClassA = SwiftComposition_ClassA(refClass: objB)
objB = nil
//WHEN
let result:Bool = objA.doAction()
//THEN
XCTAssertTrue(result)
}
func testComposition3() {
//GIVEN
var objB:SwiftComposition_ClassB! = SwiftComposition_ClassB()
let objA:SwiftComposition_ClassA = SwiftComposition_ClassA()
objA.classB = objB
//WHEN
let result:Bool = objA.doAction()
//THEN
XCTAssertTrue(result)
}
func testComposition4() {
//GIVEN
var objB:SwiftComposition_ClassB! = SwiftComposition_ClassB()
let objA:SwiftComposition_ClassA = SwiftComposition_ClassA()
objA.classB = objB
objB = nil
//WHEN
let result:Bool = objA.doAction()
//THEN
XCTAssertTrue(result)
}

Tài liệu tham khảo:

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.