पहलू उन्मुख प्रोग्रामिंग: एक रूपरेखा का उपयोग कब शुरू करें?


22

मैं सिर्फ देखा इस बात से ग्रेग युवा चुंबन चेतावनी लोग: यह रखें सरल बेवकूफ।

उनके द्वारा सुझाई गई चीजों में से एक यह है कि पहलू-उन्मुख प्रोग्रामिंग करने के लिए, किसी को एक रूपरेखा की आवश्यकता नहीं है

वह एक मजबूत बाधा बनाकर शुरू करता है: कि सभी विधियां एक लेती हैं, और केवल एक, पैरामीटर (हालांकि वह आंशिक अनुप्रयोग का उपयोग करके थोड़ी देर बाद इसे आराम करता है )।

वह जो उदाहरण देता है वह एक इंटरफ़ेस को परिभाषित करता है:

public interface IConsumes<T>
{
    void Consume(T message);
}

यदि हम एक आदेश जारी करना चाहते हैं:

public class Command
{
    public string SomeInformation;
    public int ID;

    public override string ToString()
    {
       return ID + " : " + SomeInformation + Environment.NewLine;
    }
}

आदेश के रूप में कार्यान्वित किया जाता है:

public class CommandService : IConsumes<Command>
{
    private IConsumes<Command> _next;

    public CommandService(IConsumes<Command> cmd = null)
    {
        _next = cmd;
    }
    public void Consume(Command message)
    {
       Console.WriteLine("Command complete!");
        if (_next != null)
            _next.Consume(message);
    }
}

सांत्वना के लिए लॉगिंग करने के लिए, फिर एक ही लागू होता है:

public class Logger<T> : IConsumes<T>
{
    private readonly IConsumes<T> _next;

    public Logger(IConsumes<T> next)
    {
        _next = next;
    }
    public void Consume(T message)
    {
        Log(message);
        if (_next != null)
            _next.Consume(message);
    }

    private void Log(T message)
    {
        Console.WriteLine(message);
    }
}

फिर, प्री-कमांड लॉगिंग, कमांड सर्विस और पोस्ट-कमांड लॉगिंग तो बस:

var log1 = new Logger<Command>(null);
var svr  = new CommandService(log);
var startOfChain = new Logger<Command>(svr);

और कमांड द्वारा निष्पादित किया जाता है:

var cmd = new Command();
startOfChain.Consume(cmd);

ऐसा करने के लिए, उदाहरण के लिए, PostSharp , कोई CommandServiceइस तरह से एनोटेट करेगा :

public class CommandService : IConsumes<Command>
{
    [Trace]
    public void Consume(Command message)
    {
       Console.WriteLine("Command complete!");
    }
}

और फिर विशेषता वर्ग में लॉगिंग को कुछ इस तरह से लागू करना होगा:

[Serializable]
public class TraceAttribute : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine(args.Method.Name + " : Entered!" );   
    }

    public override void OnSuccess( MethodExecutionArgs args )
    {
        Console.WriteLine(args.Method.Name + " : Exited!" );
    }

    public override void OnException( MethodExecutionArgs args )
    {
        Console.WriteLine(args.Method.Name + " : EX : " + args.Exception.Message );
    }
}

ग्रेग का उपयोग करने वाला तर्क यह है कि विशेषता से कार्यान्वयन के लिए कनेक्शन "बहुत अधिक जादू है" यह समझाने में सक्षम होना है कि एक जूनियर डेवलपर के लिए क्या हो रहा है। प्रारंभिक उदाहरण सभी "बस कोड" है और आसानी से समझाया गया है।

तो, उसके बाद लंबे समय तक बिल्ड-अप के बाद, सवाल यह है: जब आप ग्रेग के गैर-ढांचे के दृष्टिकोण से स्विच करते हैं, तो AOP के लिए PostSharp जैसी किसी चीज़ का उपयोग करने के लिए?


3
+1: निश्चित रूप से एक अच्छा सवाल है। एक बस कह सकता है "... जब आप पहले से ही इसके बिना समाधान को समझते हैं।"
स्टीवन एवर्स

1
शायद मैं केवल शैली के लिए अभ्यस्त नहीं हूं, लेकिन इस तरह से एक संपूर्ण एप्लिकेशन लिखने का विचार मुझे पूरी तरह से पागल बनाता है। मैं एक विधि अवरोधक का उपयोग करूँगा।
आरोन

@Aaronaught: हाँ, इस कारण से मैं यहाँ पोस्ट करना चाहता था। ग्रेग की व्याख्या यह है कि सिस्टम कॉन्फ़िगरेशन तब केवल सभी विभिन्न IConsumesटुकड़ों में आम CODE को जोड़ रहा है । बाहरी एक्सएमएल या कुछ धाराप्रवाह इंटरफ़ेस का उपयोग करने के बजाय --- अभी तक एक और बात सीखना है। कोई यह तर्क दे सकता है कि यह पद्धति "सीखने के लिए एक और चीज" है।
पीटर के।

मुझे अभी भी यकीन नहीं है कि मैं प्रेरणा को समझता हूं; AOP जैसी अवधारणाओं का बहुत सार , घोषित रूप से , अर्थात कॉन्फ़िगरेशन के माध्यम से चिंताओं को व्यक्त करने में सक्षम होना है। मेरे लिए यह सिर्फ स्क्वायर व्हील को रीइन्वेस्ट कर रहा है। आपकी या आपके प्रश्न की आलोचना नहीं, लेकिन मुझे लगता है कि एकमात्र समझदार उत्तर है "मैं कभी ग्रेग के दृष्टिकोण का उपयोग नहीं करूंगा जब तक कि हर दूसरा विकल्प विफल नहीं हो जाता।"
आरोन

ऐसा नहीं है कि यह मुझे बिल्कुल परेशान करता है, लेकिन क्या यह स्टैक ओवरफ्लो प्रश्न से थोड़ा अधिक नहीं होगा?
री मियाँसाक

जवाबों:


17

क्या वह "सीधे TDWTF" AOP फ्रेमवर्क लिखने की कोशिश कर रहा है? मुझे गंभीरता से अभी भी कोई सुराग नहीं मिला है कि उसकी बात क्या थी। जैसे ही आप कहते हैं "सभी तरीकों को बिल्कुल एक पैरामीटर लेना चाहिए" तो आप असफल नहीं हुए हैं? उस स्तर पर आप कहते हैं, ठीक है यह सॉफ्टवेयर लिखने की मेरी क्षमता पर कुछ गंभीर कृत्रिम बाधाओं को लगाता है, चलो अब इसे पहले छोड़ दें, तीन महीने नीचे उस रेखा के साथ जिसमें हमारे पास काम करने के लिए पूरी तरह से दुःस्वप्न कोडबेस है।

और क्या आपको पता है? आप एक साधारण विशेषता चालित IL आधारित लॉगिंग ढांचे को Mono.Cecil के साथ आसानी से लिख सकते हैं । (परीक्षण यह थोड़ा और अधिक जटिल है, लेकिन ...)

ओह और IMO, यदि आप विशेषताओं का उपयोग नहीं कर रहे हैं, तो यह AOP नहीं है। पोस्ट प्रोसेसर चरण में विधि प्रविष्टि / निकास लॉगिंग कोड को करने का पूरा बिंदु यह है कि यह आपकी कोड फ़ाइलों के साथ गड़बड़ नहीं करता है, इसलिए आपको इसके बारे में सोचने की आवश्यकता नहीं है क्योंकि आप अपना कोड रिफलेक्टर करते हैं; कि है अपनी शक्ति।

सभी ग्रेग ने यह प्रदर्शित किया है कि यह मूर्खतापूर्ण मूर्खतापूर्ण प्रतिमान है।


6
+1 यह मूर्खतापूर्ण बेवकूफी रखने के लिए। आइंस्टीन के प्रसिद्ध उद्धरण की याद दिलाता है: "जितना संभव हो उतना सरल बनाओ, लेकिन सरल नहीं।"
री मियासाका

एफडब्ल्यूआईडब्ल्यू, एफ # में एक ही प्रतिबंध है, प्रत्येक विधि अधिकतम एक तर्क पर ले जाती है।
R0MANARMY

1
let concat (x : string) y = x + y;; concat "Hello, " "World!";;ऐसा लगता है कि यह दो तर्क लेता है, मुझे क्या याद आ रही है?

2
@ द माउथ - जो वास्तव में हो रहा है, वह यह है कि concat "Hello, "आप वास्तव में एक फंक्शन बना रहे हैं y, जो सिर्फ लेता है , और x"हैलो" होने के लिए एक स्थानीय बंधन के रूप में पूर्व-परिभाषित किया गया है। यदि यह मध्यवर्ती कार्य देखा जा सकता है, तो यह कुछ ऐसा दिखाई देगा let concat_x y = "Hello, " + y। और उसके बाद, आप फोन कर रहे हैं concat_x "World!"। वाक्यविन्यास इसे कम स्पष्ट करता है, लेकिन यह आपको नए कार्यों को "सेंकना" देता है - उदाहरण के लिए let printstrln = print "%s\n" ;; printstrln "woof",। इसके अलावा, भले ही आप कुछ ऐसा करते हों let f(x,y) = x + y, यह वास्तव में सिर्फ एक तुच्छ तर्क है।
री मियासाका

1
जब मैंने विश्वविद्यालय में कोई कार्यात्मक प्रोग्रामिंग की थी, तब तक मैं F # पर एक नज़र डालूंगा, यह दिलचस्प लगता है।

8

मेरे भगवान, वह आदमी असहनीय रूप से अपघर्षक है। काश, मैं सिर्फ उस प्रश्न को देखने के बजाय आपके प्रश्न में कोड पढ़ता।

मुझे नहीं लगता कि मैं कभी इस दृष्टिकोण का उपयोग करूंगा यदि यह केवल AOP का उपयोग करने के लिए है। ग्रेग का कहना है कि यह साधारण स्थितियों के लिए अच्छा है। यहाँ मैं एक साधारण स्थिति में क्या करूँगा:

public void DeactivateInventoryItem(CommandServices cs, Guid item, string reason)
{
    cs.Log.Write("Deactivated: {0} ({1})", item, reason);
    repo.Deactivate(item, reason);
}

हाँ, मैंने इसे किया, मुझे पूरी तरह से एओपी से छुटकारा मिल गया! क्यूं कर? क्योंकि आपको साधारण स्थितियों में AOP की आवश्यकता नहीं है

एक कार्यात्मक प्रोग्रामिंग दृष्टिकोण से, प्रति फ़ंक्शन केवल एक पैरामीटर की अनुमति देना वास्तव में मुझे डराता नहीं है। फिर भी, यह वास्तव में एक डिजाइन है कि अच्छी तरह से सी # काम करता है नहीं है - और अपनी भाषा के अनाज के खिलाफ जा रहा KISS कुछ भी नहीं करता है।

अगर मैं इसके साथ शुरू करने के लिए एक कमांड मॉडल बनाना आवश्यक था तो मैं केवल इस दृष्टिकोण का उपयोग करूँगा, उदाहरण के लिए अगर मुझे पूर्ववत स्टैक की आवश्यकता है या अगर मैं WPF कमांड के साथ काम कर रहा था ।

अन्यथा, मैं सिर्फ एक रूपरेखा या कुछ प्रतिबिंब का उपयोग करूंगा। PostSharp भी सिल्वरलाइट और कम्पैक्ट फ्रेमवर्क में काम करता है - तो वह क्या कहता है "जादू" वास्तव में जादुई नहीं है सब पर

मैं कनिष्ठों को चीजों को समझाने में सक्षम होने के लिए रूपरेखा से बचने के लिए भी सहमत नहीं हूं। यह उनका कोई भला नहीं कर रहा है। अगर ग्रेग अपने जूनियर्स से इस तरह का व्यवहार करता है, जैसा कि वे मानते हैं कि उनका इलाज किया जाता है, जैसे मोटी-मोटी बेवकूफियां, तो मुझे संदेह है कि उनके वरिष्ठ डेवलपर्स या तो बहुत महान नहीं हैं, क्योंकि उन्हें शायद उनके दौरान कुछ भी सीखने का मौका नहीं दिया गया है। कनिष्ठ वर्ष।


5

मैंने एओपी पर कॉलेज में एक स्वतंत्र अध्ययन किया। मैंने वास्तव में एक एओपी को एक प्लग-इन के साथ मॉडल करने के लिए एक दृष्टिकोण पर एक पेपर लिखा था। मैं वास्तव में कुछ हद तक अप्रासंगिक हूं। मुख्य बिंदु 1 हैं) मैं युवा और अनुभवहीन था और 2) मैं एस्पेक्टज के साथ काम कर रहा था। मैं आपको बता सकता हूं कि अधिकांश एओपी फ्रेमवर्क का "जादू" जटिल नहीं है। मैंने वास्तव में उसी समय के आसपास एक परियोजना पर काम किया जो हैशटेबल का उपयोग करके एकल पैरामीटर दृष्टिकोण करने की कोशिश कर रहा था। IMO, एकल पैरामीटर दृष्टिकोण वास्तव में एक ढांचा है और यह आक्रामक है। इस पद पर भी, मैंने घोषणात्मक दृष्टिकोण की समीक्षा करने की तुलना में एकल पैरामीटर दृष्टिकोण को समझने में अधिक समय बिताया। मैं एक चेतावनी जोड़ूंगा कि मैंने फिल्म नहीं देखी है, इसलिए इस दृष्टिकोण का "जादू" आंशिक अनुप्रयोगों के उपयोग में हो सकता है।

मुझे लगता है कि ग्रेग ने आपके सवाल का जवाब दिया। जब आप सोचते हैं कि आप इस स्थिति में हैं, तो आपको अपने जूनियर डेवलपर्स को AOP फ्रेमवर्क समझाने में अत्यधिक समय बिताने की स्थिति में स्विच करना चाहिए। IMO, यदि आप इस नाव में हैं, तो आप शायद गलत जूनियर डेवलपर्स को काम पर रख रहे हैं। मुझे विश्वास नहीं है कि एओपी को एक घोषणात्मक दृष्टिकोण की आवश्यकता है, लेकिन मेरे लिए, यह डिज़ाइन के दृष्टिकोण से बहुत अधिक स्पष्ट और गैर-आक्रामक है।


+1 के लिए "मैंने घोषणात्मक दृष्टिकोण की समीक्षा करने की तुलना में एकल पैरामीटर दृष्टिकोण को समझने की कोशिश में अधिक समय बिताया।" मैंने IConsume<T>जो पूरा किया है, उसके लिए मैंने उदाहरण को जटिल पाया ।
स्कॉट व्हिटलॉक

4

जब तक मैं आपके द्वारा दिखाए गए कोड को याद नहीं कर रहा हूं, वह है 'जिम्मेदारी की श्रृंखला' डिजाइन पैटर्न, जो कि किसी वस्तु पर कार्रवाई की एक श्रृंखला को तार करने की आवश्यकता है, तो बहुत अच्छा है (जैसे कि कमांड हैंडलर की एक श्रृंखला के माध्यम से जाने वाले कमांड) क्रम।

PostSharp का उपयोग करने वाला AOP अच्छा है यदि आप संकलन समय पर जानते हैं कि आप क्या व्यवहार जोड़ना चाहते हैं। PostSharp का कोड बहुत अधिक बुनाई का मतलब है कि शून्य रन-टाइम ओवरहेड है और कोड को वास्तव में बहुत साफ रखता है (विशेषकर जब आप मल्टीकास्ट पहलुओं जैसी चीजों का उपयोग करना शुरू करते हैं)। मुझे नहीं लगता कि PostSharp का मूल उपयोग समझाने के लिए विशेष रूप से जटिल है। PostSharp का नकारात्मक पक्ष यह है कि यह सांकेतिक रूप से कई गुना बढ़ जाता है।

मैं उत्पादन कोड में दोनों तकनीकों का उपयोग करता हूं और हालांकि कुछ ओवरलैप हैं जहां उन्हें लागू किया जा सकता है मुझे लगता है कि अधिकांश भाग के लिए वे वास्तव में विभिन्न परिदृश्यों के उद्देश्य से हैं।


4

उसके विकल्प के बारे में - वहाँ किया गया, जो किया गया। एक पंक्ति विशेषता की पठनीयता की तुलना में कुछ भी नहीं है।

नए लोगों को एक छोटा व्याख्यान दें, जिसमें बताया गया है कि एओपी में चीजें कैसे काम करती हैं।


4

ग्रेग ने जो वर्णन किया है वह बिल्कुल उचित है। और इसमें खूबसूरती भी है। अवधारणा शुद्ध वस्तु अभिविन्यास की तुलना में एक अलग प्रतिमान में लागू होती है। यह अधिक प्रक्रियात्मक दृष्टिकोण या प्रवाह उन्मुख डिजाइन दृष्टिकोण है। इसलिए यदि आप विरासत कोड के साथ काम कर रहे हैं तो इस अवधारणा को लागू करना काफी मुश्किल होगा क्योंकि बहुत सारे रिफैक्टिंग की आवश्यकता हो सकती है।

मैं एक और उदाहरण देने की कोशिश करूंगा। शायद सही नहीं, लेकिन मुझे उम्मीद है कि यह बात और स्पष्ट कर देगा।

इसलिए हमारे पास एक उत्पाद सेवा है जो एक रिपॉजिटरी का उपयोग करती है (इस मामले में हम एक स्टब का उपयोग करेंगे)। सेवा को उत्पादों की एक सूची मिलेगी।

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }

    public override string ToString() { return String.Format("{0}, {1}", Name, Price); }
}

public static class ProductService
{
    public static IEnumerable<Product> GetAllProducts(ProductRepositoryStub repository)
    {
        return repository.GetAll();
    }
}

public class ProductRepositoryStub
{
    public ProductRepositoryStub(string connStr) {}

    public IEnumerable<Product> GetAll()
    {
        return new List<Product>
        {
            new Product {Name = "Cd Player", Price = 49.99m},
            new Product {Name = "Yacht", Price = 2999999m }
        };
    }
}

बेशक आप सेवा के लिए एक इंटरफ़ेस भी पास कर सकते हैं।

आगे हम एक दृश्य में उत्पादों की एक सूची दिखाना चाहते हैं। इसलिए हमें एक इंटरफ़ेस की आवश्यकता है

public interface Handles<T>
{
    void Handle(T message);
}

और एक कमांड जो उत्पादों की सूची रखती है

public class ShowProductsCommand
{
    public IEnumerable<Product> Products { get; set; }
}

और दृश्य

public class View : Handles<ShowProductsCommand>
{
    public void Handle(ShowProductsCommand cmd)
    {
        cmd.Products.ToList().ForEach(x => Console.WriteLine(x.ToString()));
    }
}

अब हमें कुछ कोड की आवश्यकता है जो यह सब निष्पादित करता है। यह हम आवेदन नामक एक वर्ग में करेंगे। रन () विधि एक एकीकृत विधि है जिसमें कोई या कम से कम व्यावसायिक तर्क नहीं है। आश्रितों को विधायक में तरीकों के रूप में इंजेक्ट किया जाता है।

public class Application
{
    private readonly Func<IEnumerable<Product>> _getAllProducts;
    private readonly Action<ShowProductsCommand> _showProducts;

    public Application(Func<IEnumerable<Product>> getAllProducts, Action<ShowProductsCommand> showProducts)
    {
        _getAllProducts = getAllProducts;
        _showProducts = showProducts;
    }

    public void Run()
    {
        var products = _getAllProducts();
        var cmd = new ShowProductsCommand { Products = products };
        _showProducts(cmd);
    }
}

अंत में हम मुख्य विधि में एप्लिकेशन की रचना करते हैं।

static void Main(string[] args)
{
    // composition
    Func<IEnumerable<Product>> getAllProducts = () => ProductService.GetAllProducts(new ProductRepositoryStub(""));
    Action<ShowProductsCommand> showProducts = (x) => new View().Handle(x);
    var app = new Application(getAllProducts, showProducts);

    app.Run();
}

अब ठंडी बात यह है कि हम मौजूदा कोड को छूने और फ्रेमवर्क या एनोटेशन के बिना लॉगिंग या अपवाद हैंडलिंग जैसे पहलुओं को जोड़ सकते हैं। अपवाद हैंडलिंग के लिए जैसे हम सिर्फ एक नया वर्ग जोड़ते हैं:

public class ExceptionHandler<T> : Handles<T>
{
    private readonly Handles<T> _next;

    public ExceptionHandler(Handles<T> next) { _next = next; }

    public void Handle(T message)
    {
        try
        {
            _next.Handle(message);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

और फिर हम आवेदन के प्रवेश बिंदु पर रचना के दौरान इसे एक साथ प्लग करते हैं। हमें एप्लिकेशन वर्ग में कोड को छूने की भी आवश्यकता नहीं है। हम सिर्फ एक पंक्ति को प्रतिस्थापित करते हैं:

Action<ShowProductsCommand> showProducts = (x) => new ExceptionHandler<ShowProductsCommand>(new View()).Handle(x);

इसलिए फिर से शुरू करने के लिए: जब हमारे पास एक प्रवाह उन्मुख डिजाइन होता है तो हम एक नई कक्षा के अंदर कार्यक्षमता जोड़कर पहलुओं को जोड़ सकते हैं। फिर हमें रचना विधि में एक पंक्ति को बदलना होगा और यही वह है।

इसलिए मुझे लगता है कि आपके प्रश्न का उत्तर यह है कि आप आसानी से एक दृष्टिकोण से दूसरे दृष्टिकोण पर स्विच नहीं कर सकते हैं, लेकिन आपको यह तय करना होगा कि आप अपने प्रोजेक्ट में किस तरह के वास्तुशिल्प दृष्टिकोण के लिए जाएंगे।

संपादित करें: वास्तव में मुझे सिर्फ एहसास हुआ कि उत्पाद सेवा के साथ उपयोग किया जाने वाला आंशिक अनुप्रयोग पैटर्न चीजों को थोड़ा और अधिक जटिल बनाता है। हमें उत्पाद सेवा पद्धति के इर्द-गिर्द एक और वर्ग लपेटने की जरूरत है ताकि वे यहां भी पहलुओं को जोड़ सकें। यह कुछ इस तरह हो सकता है:

public class ProductQueries : Queries<IEnumerable<Product>>
{
    private readonly Func<IEnumerable<Product>> _query;

    public ProductQueries(Func<IEnumerable<Product>> query)
    {
        _query = query;
    }

    public IEnumerable<Product> Query()
    {
        return _query();
    }
}

public interface Queries<TResult>
{
    TResult Query();
}

रचना को फिर इस तरह बदलना होगा:

Func<IEnumerable<Product>> getAllProducts = () => ProductService.GetAllProducts(new ProductRepositoryStub(""));
Func<IEnumerable<Product>> queryAllProducts = new ProductQueries(getAllProducts).Query;
Action<ShowProductsCommand> showProducts = (x) => new ExceptionHandler<ShowProductsCommand>(new View()).Handle(x);
var app = new Application(queryAllProducts, showProducts);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.