ควรใช้ฟังก์ชั่นที่ทำหน้าที่เป็นพารามิเตอร์หรือไม่และนำพารามิเตอร์ไปใช้กับฟังก์ชันเหล่านั้นเป็นพารามิเตอร์ด้วยหรือไม่


20

ฉันมักจะพบว่าตัวเองกำลังเขียนฟังก์ชั่นที่มีลักษณะเช่นนี้เพราะพวกเขาทำให้ฉันสามารถเข้าถึงข้อมูลได้อย่างง่ายดายและยังมีลายเซ็นที่ยอมรับพารามิเตอร์เพื่อกำหนดข้อมูลที่จะเข้าถึง

public static string GetFormattedRate(
        Func<string, RateType>> getRate,
        string rateKey)
{
    var rate = getRate(rateKey);
    var formattedRate = rate.DollarsPerMonth.ToString("C0");
    return formattedRate;
}

หรือ

public static string GetFormattedRate(
        Func<RateType, string> formatRate,
        Func<string, RateType>> getRate,
        string rateKey)
{
    var rate = getRate(rateKey);
    var formattedRate = formatRate(rate);
    return formattedRate;
}

จากนั้นฉันก็ใช้สิ่งนี้:

using FormatterModule;

public static Main()
{
    var getRate = GetRateFunc(connectionStr);
    var formattedRate = GetFormattedRate(getRate, rateType);
    // or alternatively
    var formattedRate = GetFormattedRate(getRate, FormatterModule.FormatRate, rateKey);

    System.PrintLn(formattedRate);
}

นี่เป็นเรื่องธรรมดาไหม ฉันรู้สึกว่าฉันควรทำอะไรมากกว่านี้

public static string GetFormattedRate(
        Func<RateType> getRate())
{
    var rate = getRate();
    return rate.DollarsPerMonth.ToString("C0");
}

แต่นั่นไม่ได้ผลดีนักเพราะฉันต้องสร้างฟังก์ชั่นใหม่เพื่อส่งผ่านไปยังวิธีการสำหรับทุกประเภทอัตรา

บางครั้งฉันรู้สึกว่าฉันควรจะทำ

public static string GetFormattedRate(RateType rate)
{
   return rate.DollarsPerMonth.ToString("C0");
}

แต่ดูเหมือนว่าจะนำการดึงข้อมูลออกมาและจัดรูปแบบการใช้งานซ้ำได้ เมื่อใดก็ตามที่ฉันต้องการดึงข้อมูลและจัดรูปแบบฉันต้องเขียนสองบรรทัดหนึ่งแถวเพื่อดึงข้อมูลและอีกหนึ่งรูปแบบ

ฉันขาดอะไรบ้างเกี่ยวกับการตั้งโปรแกรมการทำงาน? นี่เป็นวิธีที่ถูกต้องหรือไม่หรือมีรูปแบบที่ดีกว่าซึ่งทั้งง่ายต่อการบำรุงรักษาและใช้งานหรือไม่


50
มะเร็ง DI ได้แพร่กระจายไปแล้ว ...
Idan Arye

16
ฉันดิ้นรนเพื่อดูว่าทำไมโครงสร้างนี้จะถูกใช้ตั้งแต่แรก แน่นอนว่ามันสะดวกกว่า (และชัดเจน ) สำหรับGetFormattedRate()การยอมรับอัตราการจัดรูปแบบเป็นพารามิเตอร์เมื่อเทียบกับการยอมรับฟังก์ชั่นที่ส่งกลับอัตราการจัดรูปแบบเป็นพารามิเตอร์หรือไม่
aroth

6
วิธีที่ดีกว่าคือใช้ประโยชน์จากclosuresตำแหน่งที่คุณส่งพารามิเตอร์เองไปยังฟังก์ชันซึ่งเป็นการตอบแทนให้ฟังก์ชันที่อ้างอิงพารามิเตอร์พารามิเตอร์นั้น ฟังก์ชัน "กำหนดค่า" นี้จะถูกส่งผ่านเป็นพารามิเตอร์ไปยังฟังก์ชันซึ่งใช้งาน
โทมัสขยะ

7
@IdanArye DI มะเร็ง?
Jules

11
@Jules มะเร็งฉีดพึ่งพา
แมว

คำตอบ:


39

ถ้าคุณทำมันนานพอในที่สุดคุณจะพบว่าตัวเองเขียนฟังก์ชั่นนี้ซ้ำแล้วซ้ำอีก:

public static Type3 CombineFunc1AndFunc2(
    Func<Type1, Type2> func1,
    Func<Type2, Type3>> func2,
    Type1 input)
{
    return func2(func1(input))
}

ขอแสดงความยินดีที่คุณได้คิดค้นองค์ประกอบฟังก์ชั่น

ฟังก์ชั่น Wrapper เช่นนี้ไม่ได้ใช้ประโยชน์มากนักเมื่อมันมีความชำนาญเฉพาะกับประเภทเดียว อย่างไรก็ตามหากคุณแนะนำตัวแปรประเภทบางอย่างและละเว้นพารามิเตอร์อินพุตดังนั้นนิยาม GetFormattedRate ของคุณจะเป็นดังนี้:

public static Func<A, C> Compose(
    Func<B, C> outer, Func<A, B>> inner)
{
    return (input) => outer(inner(input))
}

var GetFormattedRate = Compose(FormatRate, GetRate);
var formattedRate = GetFormattedRate(rateKey);

สิ่งที่คุณกำลังทำมีจุดประสงค์เล็กน้อย ไม่ใช่รหัสทั่วไปดังนั้นคุณต้องทำซ้ำรหัสนั้นทุกที่ มัน overcomplicates รหัสของคุณเพราะตอนนี้รหัสของคุณมีการรวบรวมทุกสิ่งที่ต้องการจากฟังก์ชั่นเล็ก ๆ พันของตัวเอง หัวใจของคุณอยู่ในที่ที่เหมาะสม: คุณเพียงแค่ต้องคุ้นเคยกับการใช้ฟังก์ชั่นการเรียงลำดับทั่วไปที่สูงกว่าเหล่านี้เพื่อรวบรวมสิ่งต่างๆเข้าด้วยกัน หรือใช้แลมบ์ดาแฟชั่นดีเก่าที่จะเปิดFunc<A, B>และเข้าAFunc<B>

อย่าพูดซ้ำตัวเอง


16
ทำซ้ำตัวเองหากหลีกเลี่ยงการทำซ้ำตัวเองทำให้รหัสแย่ลง FormatRate(GetRate(rateKey))เช่นถ้าคุณมักจะเขียนทั้งสองเส้นแทน
user253751

6
@ กลไกฉันคิดว่าความคิดคือเขาจะสามารถใช้งานGetFormattedRateได้โดยตรงจากนี้
Carles

ฉันคิดว่านี่คือสิ่งที่ฉันพยายามทำที่นี่และฉันได้ลองใช้ฟังก์ชั่น Compose ก่อนหน้านี้ แต่ดูเหมือนว่าฉันไม่ค่อยได้ใช้เพราะฟังก์ชั่นที่ 2 ของฉันมักจะต้องการพารามิเตอร์มากกว่าหนึ่งตัว บางทีฉันต้องทำสิ่งนี้ร่วมกับการปิดสำหรับฟังก์ชั่นที่ตั้งค่าไว้ตามที่ระบุไว้โดย @ thomas-junk
rushinge

@rushinge การประพันธ์แบบนี้ทำงานในฟังก์ชั่น FP ทั่วไปซึ่งมักจะมีอาร์กิวเมนต์เดียว (อาร์กิวเมนต์เพิ่มเติมคือฟังก์ชั่นของพวกเขาเองจริงๆคิดว่ามันชอบFunc<Func<A, B>, C>); หมายความว่าคุณต้องการเพียงหนึ่งฟังก์ชันเขียนข้อความที่ใช้งานได้กับทุกฟังก์ชัน อย่างไรก็ตามคุณสามารถทำงานกับฟังก์ชั่น C # ได้ดีพอเพียงแค่ใช้การปิด - แทนการผ่านFunc<rateKey, rateType>คุณจะต้องได้อย่างแท้จริงFunc<rateType>และเมื่อผ่าน func คุณจะสร้างมัน() => GetRate(rateKey)ขึ้นมา ประเด็นก็คือคุณจะไม่เปิดเผยข้อโต้แย้งที่ฟังก์ชั่นเป้าหมายไม่สนใจ
Luaan

1
@immibis ใช่Composeฟังก์ชั่นนี้มีประโยชน์เฉพาะในกรณีที่คุณต้องการชะลอการเรียกGetRateใช้ด้วยเหตุผลบางอย่างเช่นถ้าคุณต้องการส่งผ่านCompose(FormatRate, GetRate)ไปยังฟังก์ชันที่ให้อัตราการเลือกของตัวเองเช่นใช้กับทุกองค์ประกอบใน รายการ.
jpaugh

107

ไม่มีเหตุผลใดที่จะส่งผ่านฟังก์ชั่นและพารามิเตอร์ของมันเท่านั้นที่จะเรียกมันด้วยพารามิเตอร์เหล่านั้น ในความเป็นจริงในกรณีของคุณคุณมีเหตุผลที่จะผ่านฟังก์ชั่นไม่ได้ทั้งหมด ผู้เรียกอาจเรียกฟังก์ชั่นนั้นเองและส่งผลลัพธ์

ลองคิดดู - แทนที่จะใช้:

var formattedRate = GetFormattedRate(getRate, rateType);

ทำไมไม่ใช้เพียง:

var formattedRate = GetFormattedRate(getRate(rateType));

?

เช่นเดียวกับการลดรหัสที่ไม่จำเป็นก็ยังช่วยลดการมีเพศสัมพันธ์ - ถ้าคุณต้องการที่จะเปลี่ยนวิธีเป็นอัตราที่ลึกซึ้ง (พูดถ้าgetRateขณะนี้ความต้องการทั้งสองมีปากเสียง) GetFormattedRateคุณไม่ได้มีการเปลี่ยนแปลง

ในทำนองเดียวกันมีเหตุผลที่จะไม่ได้เขียนแทนการเขียนGetFormattedRate(formatRate, getRate, rateKey)formatRate(getRate(rateKey))

อย่าทำสิ่งที่ซับซ้อนเกินไป


3
ในกรณีนี้คุณถูกต้อง แต่ถ้าฟังก์ชั่นภายในถูกเรียกหลาย ๆ ครั้งพูดในวงหรือฟังก์ชั่นแผนที่แล้วความสามารถในการส่งผ่านข้อโต้แย้งจะมีประโยชน์ หรือใช้องค์ประกอบการทำงาน / การแกงตามที่เสนอใน @Jack answer
user949300

15
@ user949300 อาจเป็นไปได้ แต่นั่นไม่ใช่กรณีการใช้งานของ OP (และถ้าเป็นเช่นนั้นอาจเป็นไปได้formatRateว่าควรถูกแมปผ่านอัตราที่ควรจัดรูปแบบ)
jonrsharpe

4
@ user949300 เฉพาะในกรณีที่ภาษาของคุณไม่สนับสนุนการปิดหรือเมื่อ lamdas มีความทะเยอทะยานที่จะพิมพ์ออกมา
Bergi

4
โปรดทราบว่าการส่งฟังก์ชั่นและพารามิเตอร์ไปยังฟังก์ชั่นอื่นเป็นวิธีที่ถูกต้องโดยสมบูรณ์ในการชะลอการประเมินในภาษาโดยไม่ต้องมีความหมายที่ขี้เกียจen.wikipedia.org/wiki/Thunk
Jared Smith

4
@JaredSmith ส่งผ่านฟังก์ชั่นใช่ผ่านพารามิเตอร์เฉพาะเมื่อภาษาของคุณไม่รองรับการปิด
user253751

15

หากคุณต้องการส่งผ่านฟังก์ชั่นเข้าสู่ฟังก์ชั่นเพราะมันผ่านการโต้แย้งพิเศษหรือเรียกมันว่าการวนซ้ำคุณสามารถส่งแลมบ์ดาแทน:

public static string GetFormattedRate(
        Func<string> getRate)
{
    var rate = getRate();
    var formattedRate = rate.DollarsPerMonth.ToString("C0");
    return formattedRate;
}

var formattedRate = GetFormattedRate(()=>getRate(rateKey));

แลมบ์ดาจะผูกข้อโต้แย้งที่ฟังก์ชั่นไม่ทราบและซ่อนไว้ว่ามีอยู่จริง


-1

นี่ไม่ใช่สิ่งที่คุณต้องการหรือ

class RateFormatter
{
    public abstract RateType GetRate(string rateKey);

    public abstract string FormatRate(RateType rate);

    public string GetFormattedRate(string rateKey)
    {
        var rate = GetRate(rateKey);
        var formattedRate = FormatRate(rate);
        return formattedRate;
    }
}

แล้วเรียกมันว่าสิ่งนี้:

static class Program
{
    public static void Main()
    {
        var rateFormatter = new StandardRateFormatter(connectionStr);
        var formattedRate = rateFormatter.GetFormattedRate(rateKey);

        System.PrintLn(formattedRate);
    }
}

หากคุณต้องการวิธีที่สามารถทำงานได้หลายวิธีในภาษาเชิงวัตถุเช่น C # วิธีปกติในการทำเช่นนั้นคือการให้วิธีการนี้เรียกวิธีนามธรรม หากคุณไม่มีเหตุผลที่เฉพาะเจาะจงที่จะทำในลักษณะที่แตกต่างคุณควรทำอย่างนั้น

สิ่งนี้ดูเหมือนเป็นวิธีแก้ปัญหาที่ดีหรือมีข้อเสียอยู่หรือเปล่า?


1
คำตอบของคุณมีสิ่งที่แปลกใหม่สองอย่าง (ทำไมฟอร์แมตเตอร์ถึงได้รับอัตราถ้าเป็นเพียงฟอร์แมตเตอร์คุณสามารถลบGetFormattedRateวิธีการและโทรออกได้IRateFormatter.FormatRate(rate)) อย่างไรก็ตามแนวคิดพื้นฐานถูกต้องและฉันคิดว่า OP เกินไปควรใช้ polymorphicaly ของรหัสถ้าเขาต้องการวิธีการจัดรูปแบบหลายวิธี
BgrWorker
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.