ডোমেন ক্লাস এবং এসকিউএল কোয়েরিগুলির মধ্যে যুক্তির সদৃশতা এড়ানোর উপায়গুলি কী কী?


21

নীচের উদাহরণটি সম্পূর্ণ কৃত্রিম এবং এর একমাত্র উদ্দেশ্য আমার বক্তব্যটি পারাপার।

ধরুন আমার কাছে একটি এসকিউএল টেবিল রয়েছে:

CREATE TABLE rectangles (
  width int,
  height int 
);

ডোমেন শ্রেণি:

public class Rectangle {
  private int width;
  private int height;

  /* My business logic */
  public int area() {
    return width * height;
  }
}

এখন ধরুন আমার ডাটাবেসের সমস্ত আয়তক্ষেত্রের মোট ক্ষেত্রটি ব্যবহারকারীকে দেখাতে হবে। আমি টেবিলের সমস্ত সারি আনতে, সেগুলিকে বস্তুতে পরিণত করে এবং তার উপরে পুনরাবৃত্তি করে এটি করতে পারি। তবে এটি দেখতে কেবল বোকা, কারণ আমার টেবিলে প্রচুর এবং প্রচুর আয়তক্ষেত্র রয়েছে।

সুতরাং আমি এটি করি:

SELECT sum(r.width * r.height)
FROM rectangles r

এটি সহজ, দ্রুত এবং ডাটাবেসের শক্তি ব্যবহার করে। যাইহোক, এটি সদৃশ যুক্তির পরিচয় দেয় কারণ আমার ডোমেন ক্লাসেও আমার একই গণনা রয়েছে।

অবশ্যই, এই উদাহরণের জন্য যুক্তির সদৃশ মোটেও মারাত্মক নয়। যাইহোক, আমি আমার অন্যান্য ডোমেন ক্লাসগুলির সাথে একই সমস্যার মুখোমুখি, যা আরও জটিল।


1
আমি সন্দেহ করি যে সর্বোত্তম সমাধানটি কোডবেস থেকে কোডবেজে বেশ বন্যভাবে পরিবর্তিত হবে, সুতরাং আপনি যে আরও জটিল উদাহরণটি সমস্যায় ফেলেছেন তার একটি সংক্ষিপ্তভাবে বর্ণনা করতে পারবেন?
Ixrec

2
@ এলএক্সেরেক: প্রতিবেদনগুলি। একটি ব্যবসায়িক অ্যাপ্লিকেশনের নিয়ম রয়েছে যা আমি ক্লাসগুলিতে ক্যাপচার করছি এবং আমার এমন প্রতিবেদন তৈরি করতে হবে যা একই তথ্য দেখায় তবে সংশ্লেষিত। ভ্যাট গণনা, অর্থ প্রদান, উপার্জন, এ জাতীয় ধরণের জিনিস।
অব্যাহতি বেগ

1
এটি কি সার্ভার এবং ক্লায়েন্টদের মধ্যে লোড বিতরণের প্রশ্ন নয়? অবশ্যই, কোনও ক্লায়েন্টের কাছে গণনার ক্যাশেড ফলাফলটি ডাম্প করা আপনার সেরা বাজি, তবে যদি ডেটা প্রায়শই পরিবর্তিত হয় এবং প্রচুর অনুরোধ থাকে, তবে ক্লায়েন্টের পরিবর্তে কেবল উপাদানগুলি এবং রেসিপিটি ফেলে দিতে সুবিধাজনক হতে পারে তাদের জন্য খাবার রান্না। আমি মনে করি যে কোনও বিতরণ সিস্টেমে একটি নির্দিষ্ট কার্যকারিতা সরবরাহ করতে পারে এমন একাধিক নোড থাকা অগত্যা খারাপ জিনিস নয়।
নাল

আমি মনে করি সর্বোত্তম উপায় হ'ল এই জাতীয় কোড তৈরি করা। আমি পরে ব্যাখ্যা করব।
জাভিয়ের কম্বেল

উত্তর:


11

যেমন lxrec উল্লেখ করেছে, এটি কোডবেস থেকে কোডবেসে আলাদা হবে। কিছু অ্যাপ্লিকেশন আপনাকে এসকিউএল ফাংশন এবং / অথবা কোয়েরিতে এই জাতীয় ব্যবসায়িক যুক্তি রাখার অনুমতি দেয় এবং আপনাকে যে কোনও সময় ব্যবহারকারীর কাছে এই মানগুলি দেখানোর প্রয়োজন হয় সেই সময় চালানোর অনুমতি দেয়।

কখনও কখনও এটি নির্বোধ মনে হতে পারে, তবে প্রাথমিক উদ্দেশ্য হিসাবে পারফরম্যান্সের চেয়ে নির্ভুলতার জন্য কোড করা ভাল।

আপনার নমুনায়, আপনি যদি কোনও ওয়েবফর্মের কোনও ব্যবহারকারীর জন্য ক্ষেত্রের মানটি দেখান, আপনার করতে হবে:

1) Do a post/get to the server with the values of x and y;
2) The server would have to create a query to the DB Server to run the calculations;
3) The DB server would make the calculations and return;
4) The webserver would return the POST or GET to the user;
5) Final result shown.

এটি নমুনার মতো সাধারণ জিনিসগুলির জন্য বোকা, তবে এটি কোনও ব্যাংকিং সিস্টেমে কোনও ক্লায়েন্টের বিনিয়োগের আইআরআর গণনা করার মতো জটিল জিনিসগুলির প্রয়োজন হতে পারে।

সঠিকতার জন্য কোড । আপনার সফ্টওয়্যারটি যদি সঠিক হয় তবে ধীর গতিতে আপনার প্রয়োজনের জায়গায় অনুকূলিত হওয়ার সম্ভাবনা রয়েছে (প্রোফাইল দেওয়ার পরে)। এটির অর্থ যদি ডাটাবেসে ব্যবসায়ের কিছু যুক্তি রাখা থাকে তবে তা হয়ে উঠুন। এজন্য আমাদের রিফ্যাক্টরিং কৌশল রয়েছে।

এটি যদি ধীরে ধীরে বা প্রতিক্রিয়াহীন হয়ে যায় তবে ডিআরওয়ির নীতি লঙ্ঘনের মতো কিছু করার জন্য আপনার কিছু অপ্টিমাইজেশন থাকতে পারে, যদি আপনি নিজেকে যথাযথ ইউনিট টেস্টিং এবং ধারাবাহিকতা পরীক্ষায় ঘিরে রাখেন তবে এটি কোনও পাপ নয়।


1
এসকিউএল-এ ব্যবসায়ের যুক্তি (প্রসেসরাল) রাখার সমস্যাটি রিফ্যাক্টরের পক্ষে অত্যন্ত বেদনাদায়ক। আপনার কাছে শীর্ষস্থানীয় এসকিউএল রিফ্যাক্টরিং সরঞ্জাম থাকলেও, তারা সাধারণত আপনার আইডিইতে কোড রিফ্যাক্টরিং সরঞ্জামগুলির সাথে ইন্টারফেস করে না (বা কমপক্ষে আমি এখনও এই ধরণের সরঞ্জামসেটটি দেখিনি)
রোল্যান্ড টেপ

2

আপনি বলছেন যে উদাহরণটি কৃত্রিম, তাই আমি জানি না যে আমি এখানে যা বলছি তা আপনার প্রকৃত পরিস্থিতির সাথে খাপ খায় তবে আমার উত্তরটি হল - কাঠামোর সংজ্ঞা এবং জিজ্ঞাসা / হেরফেরের জন্য একটি ORM (অবজেক্ট-রিলেশনাল ম্যাপিং) স্তরটি ব্যবহার করুন আপনার ডাটাবেস এইভাবে আপনার কোনও সদৃশ যুক্তি নেই, কারণ মডেলগুলিতে সমস্ত কিছু সংজ্ঞায়িত করা হবে।

উদাহরণস্বরূপ, জ্যাঙ্গো (পাইথন) কাঠামোটি ব্যবহার করে আপনি আপনার আয়তক্ষেত্রের ডোমেন শ্রেণিকে নিম্নলিখিত মডেল হিসাবে সংজ্ঞায়িত করবেন :

class Rectangle(models.Model):
    width = models.IntegerField()
    height = models.IntegerField()

    def area(self):
        return self.width * self.height

মোট অঞ্চল (কোনও ফিল্টারিং ছাড়াই) গণনা করতে আপনি সংজ্ঞায়িত করতে পারেন:

def total_area():
    return sum(rect.area() for rect in Rectangle.objects.all())

অন্যরা যেমন উল্লেখ করেছে, আপনার প্রথমে নির্ভুলতার জন্য কোড করা উচিত, এবং যখন আপনি সত্যিকার অর্থে কোনও বাধা চাপান তখনই অনুকূলিত হন। সুতরাং, যদি কোনও পরবর্তী তারিখে আপনি সিদ্ধান্ত নেন, আপনাকে একেবারে অপ্টিমাইজ করতে হবে, আপনি কোনও কাঁচা ক্যোয়ারী সংজ্ঞায়িত করতে পারেন, যেমন:

def total_area_optimized():
    return Rectangle.objects.raw(
        'select sum(width * height) from myapp_rectangle')

1

আমি একটি ধারণা ব্যাখ্যা করতে একটি নির্বোধ উদাহরণ লিখেছি:

class BinaryIntegerOperation
{
    public int Execute(string operation, int operand1, int operand2)
    {
        var split = operation.Split(':');
        var opCode = split[0];
        if (opCode == "MULTIPLY")
        {
            var args = split[1].Split(',');
            var result = IsFirstOperand(args[0]) ? operand1 : operand2;
            for (var i = 1; i < args.Length; i++)
            {
                result *= IsFirstOperand(args[i]) ? operand1 : operand2;
            }
            return result;
        }
        else
        {
            throw new NotImplementedException();
        }
    }
    public string ToSqlExpression(string operation, string operand1Name, string operand2Name)
    {
        var split = operation.Split(':');
        var opCode = split[0];
        if (opCode == "MULTIPLY")
        {
            return string.Join("*", split[1].Split(',').Select(a => IsFirstOperand(a) ? operand1Name : operand2Name));
        }
        else
        {
            throw new NotImplementedException();
        }
    }
    private bool IsFirstOperand(string code)
    {
        return code == "0";
    }
}

সুতরাং, আপনার যদি কিছু যুক্তি থাকে:

var logic = "MULTIPLY:0,1";

আপনি এটি ডোমেন শ্রেণিতে পুনরায় ব্যবহার করতে পারেন:

var op = new BinaryIntegerOperation();
Console.WriteLine(op.Execute(logic, 3, 6));

অথবা আপনার স্কয়ার-প্রজন্মের স্তরটিতে:

Console.WriteLine(op.ToSqlExpression(logic, "r.width", "r.height"));

এবং অবশ্যই, আপনি এটি সহজেই পরিবর্তন করতে পারেন। এটা চেষ্টা কর:

logic = "MULTIPLY:0,1,1,1";

-1

@ মাখাদো যেমন বলেছিলেন, এটি করার সহজতম উপায় হ'ল এটি এড়ানো এবং আপনার প্রধান জবাতে আপনার সমস্ত প্রক্রিয়াজাতকরণ করা। যাইহোক, কোড বেজ উভয়ের জন্য কোড উত্পন্ন করে নিজের স্বর পুনরাবৃত্তি না করে অনুরূপ কোডের সাথে কোড বেজ করা এখনও সম্ভব।

উদাহরণস্বরূপ কগ ব্যবহার করে একটি সাধারণ সংজ্ঞা থেকে তিনটি স্নিপেট তৈরি করতে সক্ষম করুন

স্নিপেট 1:

/*[[[cog
from generate import generate_sql_table
cog.outl(generate_sql_table("rectangle"))
]]]*/
CREATE TABLE rectangles (
    width int,
    height int
);
/*[[[end]]]*/

স্নিপেট 2:

public class Rectangle {
    /*[[[cog
      from generate import generate_domain_attributes,generate_domain_logic
      cog.outl(generate_domain_attributes("rectangle"))
      cog.outl(generate_domain_logic("rectangle"))
      ]]]*/
    private int width;
    private int height;
    public int area {
        return width * heigh;
    }
    /*[[[end]]]*/
}

স্নিপেট 3:

/*[[[cog
from generate import generate_sql
cog.outl(generate_sql("rectangle","""
                       SELECT sum({area})
                       FROM rectangles r"""))
]]]*/
SELECT sum((r.width * r.heigh))
FROM rectangles r
/*[[[end]]]*/

একটি রেফারেন্স ফাইল থেকে

import textwrap
import pprint

# the common definition 

types = {"rectangle":
    {"sql_table_name": "rectangles",
     "sql_alias": "r",
     "attributes": [
         ["width", "int"],
         ["height", "int"],
     ],
    "methods": [
        ["area","int","this.width * this.heigh"],
    ]
    }
 }

# the utilities functions

def generate_sql_table(name):
    type = types[name]
    attributes =",\n    ".join("{attr_name} {attr_type}".format(
        attr_name=attr_name,
        attr_type=attr_type)
                   for (attr_name,attr_type)
                   in type["attributes"])
    return """
CREATE TABLE {table_name} (
    {attributes}
);""".format(
    table_name=type["sql_table_name"],
    attributes = attributes
).lstrip("\n")


def generate_method(method_def):
    name,type,value =method_def
    value = value.replace("this.","")
    return textwrap.dedent("""
    public %(type)s %(name)s {
        return %(value)s;
    }""".lstrip("\n"))% {"name":name,"type":type,"value":value}


def generate_sql_method(type,method_def):
    name,_,value =method_def
    value = value.replace("this.",type["sql_alias"]+".")
    return name,"""(%(value)s)"""% {"value":value}

def generate_domain_logic(name):
    type = types[name]
    attributes ="\n".join(generate_method(method_def)
                   for method_def
                   in type["methods"])

    return attributes


def generate_domain_attributes(name):
    type = types[name]
    attributes ="\n".join("private {attr_type} {attr_name};".format(
        attr_name=attr_name,
        attr_type=attr_type)
                   for (attr_name,attr_type)
                   in type["attributes"])

    return attributes

def generate_sql(name,sql):
    type = types[name]
    fields ={name:value
             for name,value in
             (generate_sql_method(type,method_def)
              for method_def in type["methods"])}
    sql=textwrap.dedent(sql.lstrip("\n"))
    print (sql)
    return sql.format(**fields)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.