Çalışma zamanında görünüm modelleri oluşturmayı nasıl daha az acı verici hale getirebilirim?


17

Uzun soru için özür dilerim, bir rant olarak okur, ama söz veriyorum! Sorularımı aşağıda özetledim

MVC dünyasında işler açıktır. Modelin durumu vardır, Görünüm Modeli gösterir ve Denetleyici , Modelle / Modelle (temelde) bir şeyler yapar , bir denetleyicinin durumu yoktur. Bir şeyler yapmak için Denetleyicinin web hizmetleri, depo, lot üzerinde bazı bağımlılıkları vardır. Bir denetleyiciyi başlatırken, bu bağımlılıkları sağlamayı önemsiyorsunuz, başka bir şey değil. Bir eylem yürüttüğünüzde (Denetleyici'de yöntem), bu bağımlılıkları Modeli almak veya güncellemek veya başka bir etki alanı hizmetini çağırmak için kullanırsınız. Herhangi bir bağlam varsa, örneğin bazı kullanıcılar belirli bir öğenin ayrıntılarını görmek istiyorsa, o öğenin kimliğini Eylem olarak parametre olarak iletirsiniz. Denetleyicinin hiçbir yerinde herhangi bir duruma referans yoktur. Çok uzak çok iyi.

MVVM girin. WPF'yi seviyorum, veri bağlamayı seviyorum. ViewModels'e veri bağlanmasını daha da kolaylaştıran çerçeveleri seviyorum (Caliburn Micro atm kullanarak). Yine de bu dünyada işlerin daha az açık olduğunu hissediyorum. Let tekrar egzersiz yapmak: Model devlet, Görünüm vardır gösterileri ViewModel ve ViewModel yapar Modeli (temelde) ile / konumuna şeyler, bir ViewModel yapar devlet var! için (bir ya da daha Modeller belki delegeler tüm özellikleri, ama bu araçlar kendi içinde durumunu modeli bir yol ya da bir başka, başvuru olmalıdır netleştirmek için) doViewModel, web hizmetleri, depo, lot üzerinde bazı bağımlılıklara sahiptir. Bir ViewModel'i başlattığınızda, bu bağımlılıkları değil, aynı zamanda durumu da sağlamayı önemsersiniz. Ve bu bayanlar ve baylar, beni sonsuza dek rahatsız ediyor.

Eğer bir örneğini için ihtiyaç duyarsan ProductDetailsViewModelden ProductSearchViewModel(Kendisinden denilen ProductSearchWebServicesırayla döndü IEnumerable<ProductDTO>, bu şeylerden biri yapabilir? Benimle hala, herkes):

  • diyoruz new ProductDetailsViewModel(productDTO, _shoppingCartWebService /* dependcy */);bu vasıtaları, bu kötü, 3 daha bağımlılıkları hayal ProductSearchViewModelbu bağımlılıkları yanı almaya ihtiyaçları. Ayrıca yapıcıyı değiştirmek acı vericidir.
  • çağrı _myInjectedProductDetailsViewModelFactory.Create().Initialize(productDTO);kolayca çoğu IoC çerçeveler tarafından oluşturulan, fabrika sadece Func olduğunu. Bunun kötü olduğunu düşünüyorum çünkü Init yöntemleri sızdıran bir soyutlama. Ayrıca, Init yönteminde ayarlanan alanlar için salt okunur anahtar sözcüğü kullanamazsınız. Eminim birkaç sebep daha var.
  • Çağrı _myInjectedProductDetailsViewModelAbstractFactory.Create(productDTO);Yani ... Bu genellikle bu tür bir sorun için tavsiye edilir desen (soyut fabrika) 'dir. Aslında kullanmaya başlayana kadar statik yazım için özlemimi tatmin ettiğinden beri deha olsa da. Demirbaş kod miktarı çok fazla düşünüyorum (biliyorsun, kullandığım saçma değişken isimleri dışında). Çalışma zamanı parametrelerine ihtiyaç duyan her ViewModel için iki ekstra dosya (fabrika arabirimi ve uygulama) alırsınız ve çalışma zamanı olmayan bağımlılıkları 4 ekstra zaman gibi yazmanız gerekir. Ve bağımlılıklar her değiştiğinde, fabrikada da değiştirirsiniz. Artık DI kabı bile kullanmıyorum gibi geliyor. ( Bence Windsor Kalesi bunun için bir çeşit çözüme sahiptir [kendi dezavantajları varsa, yanılıyorsam beni düzeltin]).
  • anonim tür veya sözlükle bir şeyler yapın. Statik yazımı seviyorum.

Yani evet. Durum ve davranışı bu şekilde karıştırmak MVC'de hiç var olmayan bir sorun yaratır. Ve şu anda bu sorun için gerçekten yeterli bir çözüm olmadığını hissediyorum. Şimdi bazı şeyleri gözlemlemek istiyorum:

  • İnsanlar aslında MVVM kullanıyor. Yani ya yukarıdakilerin hepsini umursamıyorlar ya da başka parlak çözümleri var.
  • WPF ile MVVM'nin derinlemesine bir örneğini bulamadım. Örneğin, NDDD örnek projesi bazı DDD kavramlarını anlamama yardımcı oldu. Birisi beni MVVM / WPF için benzer bir şeye yönlendirebilirse gerçekten çok isterim.
  • Belki MVVM'yi tamamen yanlış yapıyorum ve tasarımımı tersine çevirmeliyim. Belki de bu problemi yaşamamalıydım. Başkalarının da aynı soruyu sorduğunu biliyorum, bu yüzden sadece ben değilim.

Özetlemek

  • ViewModel'in hem durum hem de davranış için bir entegrasyon noktası olması, bir bütün olarak MVVM modeliyle bazı zorlukların nedeni olduğu sonucuna varmak doğru mudur?
  • Özet fabrika modelini kullanmak, ViewModel'i statik olarak yazılan bir şekilde başlatmanın tek / en iyi yolu mu?
  • Ayrıntılı bir referans uygulaması gibi bir şey var mı?
  • Hem durum / davranışa sahip çok sayıda ViewModel sahibi olmak bir tasarım kokusu mu?

10
Bu okumak için çok uzun, gözden geçirmeyi düşünün, orada çok alakasız şeyler var. İyi cevapları kaçırabilirsiniz, çünkü insanlar bunları okumak için uğraşmazlar.
yannis

Caliburn.Micro'yu sevdiğinizi söylediniz, ancak bu çerçevenin yeni görünüm modellerini oluşturmaya nasıl yardımcı olabileceğini bilmiyor musunuz? Bazı örneklerini kontrol edin.
Euphoric

@Euphoric Biraz daha spesifik olabilir misiniz, Google burada bana yardım etmiyor gibi görünüyor. Arayabileceğim bazı anahtar kelimeler var mı?
dvdvorle

3
Bence MVC'yi biraz basitleştiriyorsunuz. Görünümün başlangıçta Modeli gösterdiğinden emin olun, ancak çalışma sırasında durumun değiştiğinden emin olun. Bence bu değişen durum bir "Modeli Düzenle" dir. Yani, Modelin tutarlılık kısıtlamaları azaltılmış düzleştirilmiş bir versiyonu. Aslında, bir Edit Model dediğim şey MVVM ViewModel. Geçiş halindeyken, daha önce MVC'deki Görünüm tarafından tutulan veya Modelin ait olmadığını düşünmediğim, taahhüt edilmemiş bir versiyonuna geri itilen durumu tutar. Yani daha önce "akışta" durumunuz vardı. Şimdi hepsi ViewModel'de.
Scott Whitlock

@ScottWhitlock Gerçekten MVC'yi basitleştiriyorum. Ama "akıdaki" durumun ViewModel'de olduğunu söylemiyorum, oradaki davranışı sıkıştırmanın, ViewModel'i kullanılabilir bir duruma, başka bir ViewModel'den başlatmayı zorlaştırdığını söylüyorum. MVC'deki "Modeli Düzenle", kendini nasıl kaydedeceğini bilmiyor (bir Kaydetme yöntemi yok). Ancak kontrolör bunu biliyor ve bunu yapmak için gereken tüm bağımlılıklara sahip.
dvdvorle

Yanıtlar:


2

Yeni bir görünüm modeli başlatırken bağımlılıklar konusu IOC ile ele alınabilir.

public class MyCustomViewModel{
  private readonly IShoppingCartWebService _cartService;

  private readonly ITimeService _timeService;

  public ProductDTO ProductDTO { get; set; }

  public ProductDetailsViewModel(IShoppingCartWebService cartService, ITimeService timeService){
    _cartService = cartService;
    _timeService = timeService;
  }
}

Konteyneri kurarken ...

Container.Register<IShoppingCartWebService,ShoppingCartWebSerivce>().As.Singleton();
Container.Register<ITimeService,TimeService>().As.Singleton();
Container.Register<ProductDetailsViewModel>();

Görünüm modelinize ihtiyacınız olduğunda:

var viewmodel = Container.Resolve<ProductDetailsViewModel>();
viewmodel.ProductDTO = myProductDTO;

Kaliburn mikro gibi bir çerçeve kullanıldığında , genellikle halihazırda mevcut olan bir çeşit IOC kabı vardır.

SomeCompositionView view = new SomeCompositionView();
ISomeCompositionViewModel viewModel = IoC.Get<ISomeCompositionViewModel>();
ViewModelBinder.Bind(viewModel, view, null);

1

ASP.NET MVC ile günlük olarak çalışıyorum ve bir yılı aşkın bir süredir WPF üzerinde çalıştım ve bu şekilde görüyorum:

MVC

Denetleyicinin eylemleri düzenlemesi gerekir (bunu getirin, ekleyin).

Görünüm, modelin görüntülenmesinden sorumludur.

Model genellikle durumu (ör. UserId, FirstName) ve durumu (ör. Başlıklar) kapsar ve genellikle görünüme özgüdür.

MVVM

Model genellikle yalnızca verileri tutar (ör. UserId, FirstName) ve genellikle etrafından geçirilir

Görünüm modeli, sunumun modelden haberdar olduğu aktif MVP modeline benzer şekilde görünümün (yöntemlerin), verilerinin (modelinin) ve etkileşimlerinin (komutları) davranışını kapsar. Görünüm modeli görünüme özgüdür (1 görünüm = 1 görünüm modeli).

Görünüm, verilerin görüntülenmesinden ve görünüm modeline veri bağlanmasından sorumludur. Bir görünüm oluşturulduğunda, genellikle onunla ilişkilendirilmiş görünüm modeli oluşturulur.


Hatırlamanız gereken şey, MVVM sunum paterninin veri bağlama özellikleri nedeniyle WPF / Silverlight'a özgü olmasıdır.

Görünüm tipik olarak hangi görünüm modeliyle ilişkili olduğunu (veya bir soyutlama ile) bilir.

Görüntüleme başına somutlaştırılmış olsa bile, görünüm modeline bir singleton gibi davranmanızı öneririm. Başka bir deyişle, bir IOC kapsayıcısı aracılığıyla DI aracılığıyla oluşturabilmeniz ve üzerine uygun yöntemleri çağırabilmeniz gerekir; modelini parametrelere göre yükler. Bunun gibi bir şey:

public partial class EditUserView
{
    public EditUserView(IContainer container, int userId) : this() {
        var viewModel = container.Resolve<EditUserViewModel>();
        viewModel.LoadModel(userId);
        DataContext = viewModel;
    }
}

Bu durumda örnek olarak, güncellenmekte olan kullanıcıya özgü bir görünüm modeli oluşturmazsınız; bunun yerine model, görünüm modelinde bir çağrı yoluyla yüklenen kullanıcıya özel veriler içerir.


Adım "Peter" ve Başlıklarım {"Rev", "Dr"} * ise, neden FirstName verilerini ve Başlık durumunu düşünüyorsunuz? Veya örneğinizi açıklığa kavuşturabilir misiniz? * gerçekten değil
Pete Kirkham

@PeteKirkham - söz konusu bir açılan kutu bağlamında bahsettiğim 'başlıklar' örneği. Genellikle kalıcı olarak bilgi gönderdiğinizde, seçim yapmak için kullanılan eyaleti (örneğin, eyaletler / iller / başlıklar listesi) göndermezsiniz. Verilerle aktarılmaya değecek herhangi bir durum (örn. Kullanımdaki kullanıcı adıdır) işlem sırasında kontrol edilmelidir, çünkü durum bayatlamış olabilir (mesaj kuyruğu gibi eşzamansız bir kalıp kullanıyorsanız).
Shelakel

Bu görevden bu yana iki yıl geçmesine rağmen, gelecekteki izleyiciler lehine bir yorum yapmalıyım: Cevabınızla ilgili iki şey beni rahatsız etti. Bir Görünüm belki bir ViewModel'e karşılık gelir, ancak bir ViewModel birkaç Views ile temsil edilebilir. İkincisi, açıkladığınız şey Servis Bulucu anti-paternidir. IMHO, görünüm modellerini her yerde doğrudan çözmemelisiniz. DI bunun için. Çözümlerinizi olabildiğince az noktada yapın. Örneğin Caliburn sizin için bu işi yapsın.
Jony Adamit

1

Sorularınız için kısa cevap:

  1. Evet Durum + Davranış bu sorunlara yol açar, ancak bu tüm OO için geçerlidir. Asıl suçlu, bir çeşit SRP ihlali olan ViewModels'in birleşmesidir.
  2. Muhtemelen statik olarak yazılmış. Ancak, diğer ViewModel'lerden ViewModels'i örnekleme ihtiyacınızı azaltmalı / ortadan kaldırmalısınız.
  3. Farkında olduğumdan değil.
  4. Hayır, ancak ilgisiz durum ve davranışa sahip ViewModels (Bazı Model referansları ve bazı ViewModel referansları gibi)

Uzun versiyon:

Aynı Sorunla karşı karşıyayız ve size yardımcı olabilecek bazı şeyler bulduk. "Büyü" çözümünü bilmememe rağmen, bu şeyler acıyı hafifletiyor.

  1. Değişiklik izleme ve doğrulama için DTO'lardan gelen ikili modelleri uygulayın. Bu "Data" -ViewModels hizmetlere bağlı olmamalı ve kaptan gelmemelidir. Onlar sadece "yeni" düzenlenebilir, aktarılabilir ve hatta DTO'dan türetilebilir. Alt satır, uygulamanıza özgü bir Model uygulamaktır (MVC gibi).

  2. ViewModellerinizi ayırın. Caliburn, ViewModels'i bir araya getirmeyi kolaylaştırır. Hatta Ekran / İletken modeli aracılığıyla öneriyor. Ancak bu bağlantı, ViewModels'i birim testini zorlaştırır, çok sayıda bağımlılık oluşturur ve en önemlisi: ViewModel yaşam döngüsünü yönetmenin yükünü ViewModels'ınıza yükler. Bunları ayırmanın bir yolu, navigasyon hizmeti veya ViewModel denetleyicisi gibi bir şey kullanmaktır. Örneğin

    genel arabirim IShowViewModels {void Show (nesne inlineArgumentsAsAnonymousType, string regionId); }

Daha da iyisi bunu bir çeşit mesajlaşma ile yapmaktır. Ancak önemli olan, ViewModel yaşam döngüsünü diğer ViewModel'lerden ele almamaktır. MVC Kontrolörlerinde birbirine bağlı değildir ve MVVM'de ViewModels birbirine bağlı olmamalıdır. Bunları başka yollarla entegre edin.

  1. Kaplarınızı "stringly" tipi / dinamik özellikleri kullanın. Her ne kadar böyle bir şey oluşturmak INeedData<T1,T2,...>ve tür güvenli oluşturma parametrelerini uygulamak mümkün olsa da buna değmez. Ayrıca her ViewModel tipi için fabrikalar oluşturmaya değmez. Çoğu IoC Konteynerleri buna çözümler sunar. Çalışma zamanında hatalar alırsınız ancak kaplin ve ünite test edilebilirliği buna değer. Hala bir tür entegrasyon testi yapıyorsunuz ve bu hatalar kolayca tespit ediliyor.

0

Genellikle bunu yapmamın yolu (PRISM kullanarak), her montaj, tüm arabirimlerin, örneklerin başlangıçta kaydedildiği bir konteyner başlatma modülü içermesidir.

private void RegisterResources()
{
    Container.RegisterType<IDataService, DataService>();
    Container.RegisterType<IProductSearchViewModel, ProductSearchViewModel>();
    Container.RegisterType<IProductDetailsViewModel, ProductDetailsViewModel>();
}

Ve örnek sınıflarınız göz önüne alındığında, konteyner bu yoldan geçerek bu şekilde uygulanacaktır. Bu şekilde, kapsayıcıya zaten erişiminiz olduğundan yeni bağımlılıklar kolayca eklenebilir.

/// <summary>
/// IDataService Interface
/// </summary>
public interface IDataService
{
    DataTable GetSomeData();
}

public class DataService : IDataService
{
    public DataTable GetSomeData()
    {
        MessageBox.Show("This is a call to the GetSomeData() method.");

        var someData = new DataTable("SomeData");
        return someData;
    }
}

public interface IProductSearchViewModel
{
}

public class ProductSearchViewModel : IProductSearchViewModel
{
    private readonly IUnityContainer _container;

    /// <summary>
    /// This will get resolved if it's been added to the container.
    /// Or alternately you could use constructor resolution. 
    /// </summary>
    [Dependency]
    public IDataService DataService { get; set; }

    public ProductSearchViewModel(IUnityContainer container)
    {
        _container = container;
    }

    public void SearchAndDisplay()
    {
        DataTable results = DataService.GetSomeData();

        var detailsViewModel = _container.Resolve<IProductDetailsViewModel>();
        detailsViewModel.DisplaySomeDataInView(results);

        // Create the view, usually resolve using region manager etc.
        var detailsView = new DetailsView() { DataContext = detailsViewModel };
    }
}

public interface IProductDetailsViewModel
{
    void DisplaySomeDataInView(DataTable dataTable);
}

public class ProductDetailsViewModel : IProductDetailsViewModel
{
    private readonly IUnityContainer _container;

    public ProductDetailsViewModel(IUnityContainer container)
    {
        _container = container;
    }

    public void DisplaySomeDataInView(DataTable dataTable)
    {
    }
}

Kapsayıcıya bir başvuru içeren tüm görünüm modellerinizin türetildiği bir ViewModelBase sınıfına sahip olmak oldukça yaygındır. Bunun yerine tüm görünüm modellerini çözme alışkanlığına sahip olduğunuz sürece new()'ing, tüm bağımlılık çözünürlüğünü çok daha basit hale getirmelidir.


0

Bazen tam bir örnek yerine en basit tanıma gitmek iyidir: http://en.wikipedia.org/wiki/Model_View_ViewModel belki ZK Java örneğini okumak C # birinden daha aydınlatıcı olabilir.

Diğer zamanlarda bağırsak içgüdülerinizi dinleyin ...

Hem durum / davranışa sahip çok sayıda ViewModel sahibi olmak bir tasarım kokusu mu?

Modelleriniz tablo başına eşleme mi? Belki de bir ORM, işi yönetirken veya birden çok tabloyu güncellerken alan nesnelerini eşlemeye yardımcı olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.