Verwenden des Repositorymusters

Verwenden des Repositorymusters in Hilo (Windows Store-Apps mit C++ und XAML)

[ Dieser Artikel richtet sich an Windows 8.x- und Windows Phone 8.x-Entwickler, die Windows-Runtime-Apps schreiben. Wenn Sie für Windows 10 entwickeln, finden Sie weitere Informationen unter neueste Dokumentation]

Aus: Umfassende Entwicklung einer Windows Store-App mit C++ und XAML: Hilo

Leitfaden-Logo

Vorherige Seite | Nächste Seite

Für Hilo C++ entschieden wir uns für die Verwendung des Repositorymusters, um die Logik, die Daten aus dem Windows 8-Dateisystem abruft, von der Darstellungslogik zu trennen, die auf diese Daten angewendet wird. App-Pflege und Komponententests sollten einfacher sein, weshalb wir uns für das Repositorymuster entschieden.

Download

Herunterladen des Hilo-Beispiels
Buch herunterladen (PDF)

Anweisungen zu dem heruntergeladenen Code finden Sie unter Erste Schritte mit Hilo.

Sie erfahren Folgendes:

  • Wie Sie das Repositorymuster für eine Windows Store-App mit C++ implementieren.
  • Wie Sie mit dem Repositorymuster Komponententests für die App ausführen.

Betrifft

  • Windows-Runtime für Windows 8
  • Visual C++-Komponentenerweiterungen (C++/CX)
  • XAML

Einführung

Das Repositorymuster trennt die Logik, mit der Daten abgerufen, beibehalten und dem zugrunde liegenden Modell zugeordnet werden, von der Geschäftslogik zum Bearbeiten der Daten. Aufgaben des Repositorys:

  • Vermitteln zwischen der Datenquelle und den Geschäftsebenen der App.
  • Abfragen der Datenquelle.
  • Zuordnen der Daten aus der Datenquelle zum Modell.
  • Übernehmen von im Modell vorgenommenen Änderungen in der Datenquelle.

Die Trennung der Geschäftslogik von der Datenquelle bietet folgende Vorteile:

  • Zentralisieren des Zugriffs auf die zugrunde liegende Datenquelle über eine Datenzugriffsschicht.
  • Isolieren der Datenzugriffsschicht für die Unterstützung von Komponententests.
  • Verbessern der Verwaltbarkeit von Code durch Trennen der Geschäftslogik von der Datenzugriffslogik.

Weitere Informationen finden Sie unter Das Repositorymuster.

Hilo implementiert das Repositorymuster durch das Definieren einer abstrakten Basisklasse, die über reine virtuelle Memberfunktionen verfügt. Diese Funktionen stellen eine Basisklasse bereit, von der andere Repositoryklassen erben müssen. Eine reine virtuelle Memberfunktion ist als virtuell definiert, und sie ist dem Wert 0 zugewiesen. Solch eine abstrakte Basisklasse kann nicht zum Instanziieren von Objekten verwendet werden, und sie dient ausschließlich als Schnittstelle. Wenn eine Unterklasse dieser abstrakten Klasse instanziiert werden muss, muss sie daher jede der virtuellen Funktionen implementieren. Dies führt dazu, dass die Unterklasse die von der abstrakten Basisklasse definierte Schnittstelle unterstützt.

Die abstrakte Repository-Basisklasse definiert eine Reihe von reinen virtuellen Memberfunktionen, die von den Memberfunktionen überschrieben werden müssen, die in einer abgeleiteten Klasse über die gleichen Signaturen verfügen. Die FileSystemRepository-Klasse wird von der Repository-Klasse abgeleitet und überschreibt die reinen virtuellen Memberfunktionen, um das Dateisystem nach Fotos abzufragen. Anschließend wird von der TileUpdateScheduler-Klasse und Ansichtsmodellklassen eine freigegebene Instanz der FileSystemRepository-Klasse verwendet, um Fotos aus dem Dateisystem zu lesen. Diese Beziehung wird in der folgenden Abbildung veranschaulicht.

Repositoryblockdiagramm

Die StubRepository-Klasse implementiert außerdem die in der abstrakten Repository-Basisklasse definierten reinen virtuellen Memberfunktionen, um für Komponententests eine Pseudorepositoryimplementierung bereitzustellen. Dann kann eine Instanz der StubRepository-Klasse aus Komponententests an die Ansichtsmodellklassen übergeben werden. Diese Pseudoklasse gibt keine Fotos aus dem Dateisystem, sondern lediglich Pseudofotoobjekte zurück. Weitere Informationen über Komponententests finden Sie unter Testen der App.

In der folgenden Abbildung wird eine Übersicht über die Repositoryarchitektur gezeigt. Der Hauptvorteil dieser Architektur ist, dass ein Repository mit Zugriff auf eine Datenquelle, z. B. das Dateisystem, bequem durch ein Repository ersetzt werden kann, das auf eine andere Datenquelle, z. B. die Cloud, zugreift.

Stub-Klassen für Komponententests

Um ein neues Repository zu implementieren, das von der abstrakten Repository-Basisklasse abgeleitet wird, müssen Sie auch die Schnittstellen IPhoto, IPhotoGroup, IYearGroup und IMonthBlock implementieren. Diese Schnittstellen werden in Hilo von der Klasse Photo, HubPhotoGroup, MonthGroup, YearGroup bzw. MonthBlock implementiert.

Um zu erläutern, wie Hilo die Repository-Klasse implementiert und verwendet, haben wir eine exemplarische Vorgehensweise für den Code bereitgestellt, in der gezeigt wird, wie die Seite %%Rotate%% mit der Repository-Klasse ein Foto aus dem Dateisystem abruft.

[Oben]

Exemplarische Vorgehensweise für den Code

Die App-Klasse in Hilo enthält die Membervariable m_repository vom Typ Repository, die als freigegebener Zeiger vom Typ FileSystemRepository im Klassenkonstruktor instanziiert wird. Diese Instanz wird als freigegebener Zeiger erstellt. Daher enthält die App nur eine einzelne Instanz der FileSystemRepository-Klasse, die zwischen den erforderlichen Klassen übergeben wird.

App.xaml.cpp


m_exceptionPolicy = ExceptionPolicyFactory::GetCurrentPolicy();
m_repository = std::make_shared<FileSystemRepository>(m_exceptionPolicy);


Die OnLaunched-Methode der App-Klasse übergibt die freigegebene Zeigerinstanz des FileSystemRepository an die ScheduleUpdateAsync-Methode der TileUpdateScheduler-Klasse. Die ScheduleUpdateAsync-Methode verwendet diese freigegebene Zeigerinstanz zum Abrufen von Fotos, aus denen Miniaturansichten für die Live-Kachel der App generiert werden.

Die App-Klasse macht über die GetRepository-Methode eine Singleton-Instanz der FileSystemRepository-Klasse verfügbar, die vom Konstruktor der ViewModelLocator-Klasse aufgerufen wird, um die freigegebene Zeigerinstanz der FileSystemRepository-Klasse abzurufen und zu speichern.

ViewModelLocator.cpp


m_repository = safe_cast<Hilo::App^>(Windows::UI::Xaml::Application::Current)->GetRepository();


Da sowohl die ViewModelLocator-Klasse als auch die TileUpdateScheduler-Klasse Repositorys vom Typ shared_ptr<Repository> erwarten, können stattdessen alle Repositoryimplementierungen verwendet werden, die die abstrakte Repository-Basisklasse implementieren.

Die ViewModelLocator-Klasse verfügt über Eigenschaften, die für jede Seite der App ein Ansichtsmodellobjekt abrufen. Weitere Informationen Sie unter Verwenden des MVVM-Musters. Im folgenden Codebeispiel wird die RotateImageVM-Eigenschaft der ViewModelLocator-Klasse gezeigt.

ViewModelLocator.cpp


RotateImageViewModel^ ViewModelLocator::RotateImageVM::get()
{
    return ref new RotateImageViewModel(m_repository, m_exceptionPolicy);
}


Die RotateImageVM-Eigenschaft erstellt eine neue Instanz der RotateImageViewModel-Klasse und übergibt die freigegebene Zeigerinstanz der FileSystemRepository-Klasse an den RotateImageViewModel-Konstruktor. Anschließend wird die freigegebene Zeigerinstanz der FileSystemRepository-Klasse in der m_repository-Membervariablen der RotateImageViewModel-Klasse gespeichert, wie im folgenden Codebeispiel gezeigt.

RotateImageViewModel.cpp


RotateImageViewModel::RotateImageViewModel(shared_ptr<Repository> repository, shared_ptr<ExceptionPolicy> exceptionPolicy) : 
    ImageBase(exceptionPolicy), m_repository(repository), m_imageMargin(Thickness(0.0)), m_getPhotoAsyncIsRunning(false),
    m_inProgress(false), m_isSaving(false), m_rotationAngle(0.0)


Das Image-Steuerelement in der RotateImageView-Klasse wird an die Photo-Eigenschaft der RotateImageViewModel-Klasse gebunden. Die Photo-Eigenschaft wiederum ruft die GetImagePhotoAsync-Methode auf, um das Foto für die Anzeige abzurufen, wie im folgenden Codebeispiel gezeigt.

RotateImageViewModel.cpp


concurrency::task<IPhotoImage^> RotateImageViewModel::GetImagePhotoAsync()
{
    assert(IsMainThread());
    return m_repository->GetSinglePhotoAsync(m_photoPath);
}


GetImagePhotoAsync ruft GetSinglePhotoAsync für die freigegebene Zeigerinstanz der FileSystemRepository-Klasse auf, die in der m_repository-Membervariablen gespeichert ist.

[Oben]

Abfragen des Dateisystems

Hilo verwendet zum Abfragen der Bildbibliothek nach Fotos die FileSystemRepository-Klasse. Die Ansichtsmodelle verwenden diese Klasse, um Fotos für die Anzeige bereitzustellen.

Beispielsweise verwendet die RotateImageViewModel-Klasse die GetImagePhotoAsync-Methode, um ein Foto für die Anzeige auf der Seite %%Rotate%% zurückzugeben. Diese Methode wiederum ruft die GetSinglePhotoAsync-Methode im FileSystemRepository auf. Hier ist der Code.

FileSystemRepository.cpp


task<IPhotoImage^> FileSystemRepository::GetSinglePhotoAsync(String^ photoPath)
{
    assert(IsMainThread());
    String^ query = "System.ParsingPath:=\"" + photoPath + "\"";    
    auto fileQuery = CreateFileQuery(KnownFolders::PicturesLibrary, query, IndexerOption::DoNotUseIndexer);
    shared_ptr<ExceptionPolicy> policy = m_exceptionPolicy;
    return create_task(fileQuery->GetFilesAsync(0, 1)).then([policy](IVectorView<StorageFile^>^ files) -> IPhotoImage^
    {
        if (files->Size > 0)
        {
            IPhotoImage^ photo = (ref new Photo(files->GetAt(0), ref new NullPhotoGroup(), policy))->GetPhotoImage();
            create_task(photo->InitializeAsync());
            return photo;
        }
        else
        {
            return nullptr;
        }
    }, task_continuation_context::use_current());
}


Die GetSinglePhotoAsync-Methode ruft CreateFileQuery auf, um eine Dateiabfrage zu erstellen. Damit werden unter Verwendung der erweiterten Abfragesyntax Informationen zum Dateisystem abgerufen. Zum Ausführen der Abfrage wird dann GetFilesAsync aufgerufen, und für den sich ergebenden asynchronen Vorgang wird eine IVectorView-Sammlung mit StorageFile-Verweisen zurückgegeben. Jedes StorageFile-Objekt stellt eine Datei im Dateisystem dar, die mit der Abfrage übereinstimmt. In diesem Fall enthält die IVectorView-Sammlung maximal ein StorageFile-Objekt, da GetFilesAsync nur eine Datei zurückgibt, die mit der Abfrage übereinstimmt. Anschließend erstellt und initialisiert die wertbasierte Fortsetzung, die im Hauptthread ausgeführt wird, eine Instanz der PhotoImage-Klasse, die die einzelne von GetFilesAsync zurückgegebene Datei darstellt. Die PhotoImage-Instanz wird dann von GetSinglePhotoAsync an GetImagePhotoAsync in der RotateImageViewModel-Klasse zurückgegeben.

[Oben]

Erkennen von Dateisystemänderungen

Manche Seiten in der App reagieren auf Dateisystemänderungen in der Bildbibliothek, während die App ausgeführt wird. Die Hubseite, Abbildbrowserseite und Bildansichtsseite werden aktualisiert, nachdem eine zugrunde liegende Dateisystemänderung der Bildbibliothek erkannt wurde. Die Hubseite und die Abbildbrowserseite werden nur alle 30 Sekunden aktualisiert, auch wenn Dateiänderungsbenachrichtigungen in kürzeren Intervallen eintreffen. So soll verhindert werden, dass Seiten zu häufig aktualisiert werden, wenn der Bildbibliothek Bilder hinzugefügt werden, während die App ausgeführt wird.

Um dies zu implementieren, erstellen die Klassen HubPhotoGroup, ImageBrowserViewModel und ImageViewModel in ihrem Konstruktor jeweils ein function-Objekt, das aufgerufen wird, wenn Fotos in den abgefragten Ordnern hinzugefügt, gelöscht oder geändert werden. Anschließend wird das function-Objekt an die AddObserver-Methode der FileSystemRepository-Klasse übergeben. Dies ist der Code.

ImageViewModel.cpp


auto wr = WeakReference(this);
function<void()> callback = [wr] {
    auto vm = wr.Resolve<ImageViewModel>();
    if (nullptr != vm)
    {
        vm->OnDataChanged();
    }
};
m_repository->AddObserver(callback, PageType::Image);


Dieser Code ermöglicht es, dass die OnDataChanged-Methode im ImageViewModel-Objekt aufgerufen wird, wenn sich das zugrunde liegende Dateisystem während der Ausführung der App ändert. Beim Aufruf der OnDataChanged-Methode fragt diese das Dateisystem erneut ab, um die Fotominiaturansichten auf dem Bildstreifen zu aktualisieren. Die AddObserver-Methode in der FileSystemRepository-Klasse speichert lediglich das function-Objekt zur späteren Verwendung in einer Membervariablen.

Wenn die QueryPhotosAsync-Methode in der ImageViewModel-Klasse aufgerufen wird, um die auf dem Bildstreifen anzuzeigenden Fotos abzurufen, wird die GetPhotosForDateRangeQueryAsync-Methode in der FileSystemRepository-Klasse aufgerufen. Diese Methode ruft die Bildbibliothek nach Fotos in einem bestimmten Datumsbereich ab und gibt die übereinstimmenden Fotos zurück. Nachdem die Abfrage erstellt wurde, wird ein QueryChange-Objekt erstellt, um ein function-Objekt zu registrieren, das aufgerufen wird, wenn sich die Ergebnisse der Dateisystemabfrage ändern. Dies ist der Code.

FileSystemRepository.cpp


m_allPhotosQueryChange = (m_imageViewModelCallback != nullptr) ? ref new QueryChange(fileQuery, m_imageViewModelCallback) : nullptr;


Mit der QueryChange-Klasse wird die Abfrage nach Dateisystemänderungen überwacht. Der Konstruktor akzeptiert eine Dateiabfrage und ein function-Objekt. Anschließend registriert er einen Handler für das ContentsChanged-Ereignis, das ausgelöst wird, wenn im abgefragten Ordner ein Element hinzugefügt, gelöscht oder geändert wird. Beim Auslösen dieses Ereignisses wird das an die Klasse übergebene function-Objekt ausgeführt.

Dies führt dazu, dass auf dem Bildstreifen der Bildansichtsseite eine Auflistung von Fotominiaturansichten, die im angegebenen Datumsbereich liegen, sowie das vollständige Foto für die ausgewählte Miniaturansicht angezeigt werden. Wenn Fotos aus diesem Datumsbereich dem Dateisystem hinzugefügt, aus dem Dateisystem gelöscht oder anderweitig geändert werden, wird die App benachrichtigt, dass der entsprechende Dateisysteminhalt geändert wurde, und die Miniaturansicht wird aktualisiert.

Hinweis  Der Handler für dieses Ereignis bleibt registriert, wenn die App angehalten wird. Die App empfängt jedoch keine ContentsChanged-Ereignisse, während sie angehalten ist. Stattdessen wird die aktuelle Ansicht aktualisiert, wenn die App fortgesetzt wird.
 

Wenn die angehaltene App fortgesetzt wird, ruft die OnResume-Methode in der App-Klasse die NotifyAllObservers-Methode in der FileSystemRepository-Klasse auf, die wiederum die function-Objekte für die Hub-, Abbildbrowser- und Bildansichtsseiten aufruft, damit der relevante Inhalt aktualisiert wird.

[Oben]

 

 

Anzeigen:
© 2017 Microsoft