【问题标题】:best practice for how to inherit property from parent class and override setter and getter如何从父类继承属性并覆盖 setter 和 getter 的最佳实践
【发布时间】:2015-04-11 02:46:00
【问题描述】:

我正在尝试使用继承样式重新构建我的项目代码,如何从父类继承属性并覆盖 setter 和 getter 的最佳做法是什么?

我给出demo代码,demo中ChartModel是ChartViewController中的基类,LineChartModel是LineChartViewController中ChartModel的子类。

我想在子视图控制器中覆盖LineChartModel *dataModel 的setter 和getter。请包括任何@synthesize 和受保护的实例变量,或者如果它是由编译器自动生成的,请标记。提前致谢。

// ChartModel.h
@interface ChartModel : NSObject
-(BOOL)hasData;
@end

// LineChartModel.h
@interface LineChartModel : chartModel
-(void)getLineColor;
@property (nonatomic, strong) NSArray* dataArray;
@end

// ChartViewController.h
@interface ChartViewController: UIViewController
@property (nonatomic, strong) ChartModel *dataModel;
-(void)updateUI;
@end

// ChartViewController.m
@implementation ChartViewController
-(void)updateUI {
    if ([self.dataModel hasData]) {
        [self.view setHidden:NO];
    } else {
        self.view.hidden = YES;
    }
// setter and getter here
@end

// LineChartViewController.h
@interface LineChartViewController : ChartViewController

// pay attension here, same name but a sub class of chartModel
@property (nonatomic, strong) LineChartModel *dataModel; 

@end

// LineChartViewController.m
@implementation LineChartViewController

//override dataModel setter here
//override dataModel getter here

@end

【问题讨论】:

    标签: ios objective-c


    【解决方案1】:

    从技术上讲,您在实现LineChartViewController 时唯一需要的是:

    @dynamic dataModel;
    

    这告诉编译器 getter 和 setter 将以某种无法立即看到的方式提供。实际上,它们将由超类提供。

    但是,这会带来问题。一个LineChartViewController 是一个ChartViewController。这意味着LineChartViewController 的实例可以传递给声明为采用ChartViewController 的方法或函数,并且该方法/函数有权对​​其执行ChartViewController 接口允许的任何操作。这包括将ChartModel(不是LineChartModel)的实例分配给它的dataModel 属性。据推测,如果dataModel 属性不是LineChartModelLineChartViewController 将中断。

    从技术角度来说,您的设计违反了Liskov substitution principle

    这不是对设计问题的修复,但如果它发生,您可以在运行时捕获问题,方法是实现这样的 setter 覆盖:

    - (void) setDataModel:(LineChartModel*)dataModel
    {
        if (dataModel && ![dataModel isKindOfClass:[LineChartModel class]])
        {
            NSString* reason = [NSString stringWithFormat:@"%@ is not a valid dataModel for LineChartViewController; it must be a kind of LineChartModel", dataModel];
            [[NSException exceptionWithName:NSInvalidArgumentException reason:reason userInfo:nil] raise];
        }
        [super setDataModel:dataModel];
    }
    

    【讨论】:

    • 谢谢!目的是ChartViewController中的一些UI方法需要self.dataModel,我想继承这些方法,所以子类中没有重复代码,这样也行吗?我更新了我的问题,检查updateUIfunc
    • 能否也说明一下,如何在子类中声明实例变量_dataModel?类扩展中可以是 LineChartModel *_dataModel 吗?
    • 是的,这会很好。但是,它提供了更大的灵活性并避免了 dataModel 属性的这个问题,只需要一个方法即可。例如- (BOOL) shouldBeHidden 或(不太具体的)- (BOOL) hasData。然后,子类可以随心所欲地实现它。它可能有dataModel 属性,也可能没有,超类不关心它的类型。
    • 您的设计违反了子类型中方法参数的逆变性。允许-setDataModel: 采用更广泛的类型,但您试图使其采用更窄的类型。正如我所描述的,可以将ChartModel 实例分配给LineChartViewController 实例的dataModel 属性(因为它只能通过指向ChartViewController 的指针知道它)但这会破坏事情。
    • LineChartViewController 中没有针对 dataModel 属性的实例变量。它使用来自ChartViewController 的实例变量。事实上,它也使用访问器。它只覆盖了访问器的类型。 (实例变量的类型无关紧要,因为在运行时,它只是一个指向对象类型的指针,它们都是可以互换的。)
    猜你喜欢
    • 1970-01-01
    • 2011-03-21
    • 2011-03-11
    • 1970-01-01
    • 2018-11-08
    • 2013-11-11
    • 2014-11-18
    • 2012-12-24
    • 1970-01-01
    相关资源
    最近更新 更多