2016-09-04 6 views
1

Я недавно наткнулся на случай, когда было бы очень удобно преобразовать экземпляр класса в подкласс, в то время как экземпляр был создан в родительском классе. Но я никогда такого не видел. Так есть ли способ сделать что-то вроде:Можно ли преобразовать экземпляр класса в экземпляр подкласса?

class Foo { 
    var name: String 
} 

class Bar: Foo { 
    var friendName: String 
} 

let foo = Foo(name: "Alice") 
foo.toBar(friendName: "Bob") 
// foo now of type Bar, as if I'd done 
// foo = Bar(name: "Alice", friendName: "Bob") 

Если это не возможно, есть несколько причин, это было бы невозможно с точки зрения дизайна?

=== редактировать === описание случая использования, где это может иметь смысл

Пусть говорит, что есть две точки зрения, представляющее то, что соответствует одной и той же записи базы данных для книги, на это только предварительный просмотр книга, а другая - более сложный вид. Модели могут быть:

protocol BookMetaDelegate { 
    func onReadStatusUpdate() 
} 

/// describe a book 
class BookMeta { 
    var delegate: BookMetaDelegate? 
    private var _hasBeenRead: Bool 
    var hasBeenRead: Bool { 
    get { 
     return _hasBeenRead 
    } 
    set { 
     guard newValue != _hasBeenRead else { return } 
     _hasBeenRead = newValue 
     delegate?.onReadStatusUpdate() 
    } 
    } 
    var title: String 
} 

/// contains all the content of a book 
class Book: BookMeta { 
    var content: BookContent 
    var lastPageRead: Int 

    /// some logic that only makes sense in a Book instance 
    func getLastPageRead() { 
    return content.getPage(lastPageRead) 
    } 
} 

и взгляды могут выглядеть следующим образом:

class BookPreview: UIView, BookMetaDelegate { 
    var book: BookMeta 
    init(book: BookMeta) { 
    book.delegate = self 
    } 
    func onReadStatusUpdate() { 
    print("read status has changed! UI should update") 
    } 
} 

class BookView: UIView { 
    var book: Book 
    init(book: Book) { 
    book.hasBeenRead = true 
    } 
} 

Тогда все может случиться, как

fetch(bookMetaWithId: 123).then { bookMeta in // bookMeta is of type BookMeta 
    let preview = BookPreview(book: bookMeta) 
    ... 

    fetch(contentOf: bookMeta).then { content, lastPageRead in 
    bookMeta.asBook(content: content, lastPageRead: lastPageRead) 
    let bookView = BookView(book: bookMeta) // doing so will change the hasBeenRead flag and message the instance's delegate, ie the preview 
    ... 
    } 
} 
+0

Нет, это невозможно. Можно было бы создать * новый * 'Bar' из существующего' Foo', но вы не можете изменить тип существующего объекта, и для этого не было бы никакой пользы. –

+0

Почему не было никакой пользы для этого? Это более точка зрения. Предположим, у меня есть два класса, которые представляют профиль пользователя и пользователя с большим количеством другой информации. Сначала я загружаю профиль, а позже получаю другую информацию. Тогда я мог бы отказаться от моего экземпляра UserProfile, используя дополнительные данные. – Guig

+1

Вы используете «downcast» неправильно - вы опускаете фактический тип объекта, а не на другой. В вашем примере нет преимущества перед созданием нового объекта или создания «другой информации» (возможно) необязательного свойства в вашем исходном классе. Вот почему нет никакой пользы. Кажется, что «два класса, представляющие профиль пользователя и пользователя с большим количеством других сведений», являются реальной проблемой. Создание лучшего дизайна гораздо более разумно, чем полное разрушение системы типов. –

ответ

0

Думая о нем, похоже, что если такое вообще возможно , он нарушил бы такие вещи, как:

class Foo { 
    var name: String 
} 

class Bar: Foo { 
    var friendName: String 
} 

class Bla: Foo { 
    var surname: String 
} 

func something(foo: Foo) { 
    foo.toBla(surname: "Will") 
} 

let bar = Bar(name: "Alice", friendName: "Bob") 
something(foo: bar) // what does that do ??? is bar a Bla now ? 

, так что это было бы хорошей причиной для того, чтобы сделать такое литье невозможным.