שאלות ומידע לקראת ראיון עבודה של מפתח Java

מהם עקרונות מוצקים?

מהו עיקרון אחריות יחידה (SRP):

לכיתה צריכה להיות רק אחריות אחת.
צריכה להיות לזה רק סיבה אחת לשנות.
היתרונות כוללים בדיקה קלה יותר, צימוד נמוך יותר וארגון טוב יותר.
דוּגמָה:

Java

ספר בכיתה ציבורית {
שם מחרוזת פרטי;
מחבר מחרוזת פרטי;
טקסט מחרוזת פרטי;
// קונסטרוקטורים, גטרים ומגדירים
}

מהו עיקרון פתוח-סגור (OCP):

ישויות תוכנה (מחלקות, מודולים, פונקציות) צריכות להיות פתוחות להרחבה אך סגורות לצורך שינוי.
השג זאת באמצעות ממשקים, מחלקות מופשטות וירושה.
דוּגמָה:
Java

ממשק Shape {
אזור כפול();
}
class Circle מיישמת Shape { /* יישום / } מחלקה מלבן מיישמת Shape {/ יישום */ }

מהו עקרון החלפת ליסקוב (LSP):

תת-סוגים חייבים להיות ניתנים להחלפה עבור סוגי הבסיס שלהם מבלי לשנות את נכונות התוכנית.
עוקב אחר מערכת היחסים “הוא-א”.
דוּגמָה:
Java

class Bird { /* התנהגות נפוצה של ציפורים / } מחלקה דרור מרחיב ציפור { / התנהגות דרור ספציפית */ }

מהו עקרון הפרדת הממשק (ISP):

אין להכריח לקוחות להיות תלויים בממשקים שהם לא משתמשים בהם.
פיצול ממשקים גדולים לממשקים קטנים יותר וספציפיים יותר.
דוּגמָה:
Java

ממשק עובד {
void work();
}
ממשק Eater {
void eat();
}
class Robot מיישמת Worker { /* יישום */ }

מהו עקרון היפוך תלות (DIP):

מודולים ברמה גבוהה לא צריכים להיות תלויים במודולים ברמה נמוכה; שניהם צריכים להיות תלויים בהפשטות.
הפשטות לא צריכות להיות תלויות בפרטים; הפרטים צריכים להיות תלויים בהפשטות.

// הפשטה
ממשק MessageSender {
void sendMessage(הודעה מחרוזת);
}

// מודול ברמה גבוהה
class NotificationService {
הודעה פרטיתSender messageSender;

public NotificationService(MessageSender messageSender) {
this.messageSender = messageSender;
}
public void send(String message) {
messageSender.sendMessage(message);
}

}

// מודול ברמה נמוכה
class EmailSender מיישם את MessageSender {
@עקוף
public void sendMessage(הודעה מחרוזת) {
System.out.println(“שליחת דוא”ל: ” + הודעה);
}
}

// מודול נוסף ברמה נמוכה
class SmsSender מיישמת MessageSender {
@עקוף
public void sendMessage(הודעה מחרוזת) {
System.out.println(“שליחת SMS: ” + הודעה);
}
}

// כיתה ראשית
כיתה ציבורית ראשי {
public static void main(String[] args) {
// השתמש ב-EmailSender
MessageSender emailSender = new EmailSender();
NotificationService notificationService1 = new NotificationService(emailSender);
notificationService1.send(“שלום באמצעות דואר אלקטרוני!”);

    // Use SmsSender
    MessageSender smsSender = new SmsSender();
    NotificationService notificationService2 = new NotificationService(smsSender);
    notificationService2.send("Hello via SMS!");
}

}

ממשק MessageSender משמש כהפשטה.
מודול ברמה גבוהה NotificationService תלוי בהפשטה זו במקום ביישומים קונקרטיים.
מודולים ברמה נמוכה כמו EmailSender ו-SmsSender תלויים גם הם בהפשטה על ידי יישום ממשק MessageSender.

מהי נורמליזציה של SQL

מהי נורמליזציה?
נורמליזציה כוללת מבנה נתונים בתוך מסד נתונים יחסי כדי לחסל חריגות כמו יתירות.
הוא מפרק טבלאות גדולות ומורכבות לטבלאות קטנות יותר תוך שמירה על קשרי נתונים.
מדוע נורמליזציה חשובה?
מפחית יתירות: על ידי פיצול נתונים לטבלאות קטנות יותר, אנו נמנעים מאחסון אותו מידע מספר פעמים.
משפר את ביצועי השאילתה: טבלאות מנורמלות קטנות יותר מובילות לביצוע מהיר יותר של שאילתה.
ממזער חריגות עדכון: ניתן לבצע עדכונים מבלי להשפיע על רשומות אחרות.
משפר את שלמות הנתונים: מבטיח נתונים עקביים ומדויקים.
תרחישים לנורמליזציה:
שלמות נתונים: מנע חוסר עקביות בעת עדכון נתונים (למשל, שינויים בגיל הלקוח).
שאילתה יעילה: פשט שאילתות מורכבות על ידי פירוק נתונים.
אופטימיזציה של אחסון: הסר יתירות מיותרת כדי לחסוך במקום.

מהו מונוליט של Microservices?

מהו מונולית?

יישום מונוליטי בנוי כיחידה אחת ומאוחדת. הוא מחבר היטב את כל הרכיבים (כגון ממשק המשתמש בצד הלקוח, מסד הנתונים והאפליקציה בצד השרת) לבסיס קוד אחד.
יתרונות:
פריסה קלה: מכיוון שמדובר בקובץ הפעלה או ספריה בודדים, הפריסה פשוטה.
מהירות פיתוח: בשלב מוקדם בחייו של פרויקט, מונוליטים מאפשרים פיתוח מהיר בשל הפשטות שלהם.
ביצועים: קוד מרכזי יכול להתמודד עם פונקציות שונות.

מה הם Microservices?

שירותי מיקרו מפרקים את האפליקציה לשירותים קטנים יותר הניתנים לפריסה עצמאית. כל שירות מתמקד בתחום עסקי ספציפי.
יתרונות:
מדרגיות: שירותי מיקרו מאפשרים שינוי קנה מידה של רכיבים בודדים באופן עצמאי.
גמישות: מפתחים יכולים לבחור טכנולוגיות שונות עבור כל שירות.
בידוד: כשלים בשירות אחד אינם משפיעים על אחרים.
אתגרים:
מורכבות: ניהול שירותים מרובים דורש תזמור חזק.
תקורה ברשת: תקשורת בין שירותים מציגה חביון.
מורכבות הפריסה: תיאום עדכונים בין שירותים יכול להיות מסובך.

מה זה פולימורפיזם?

דריסת שיטה:
כאשר תת-מחלקה מספקת מימוש ספציפי למתודה שכבר מוגדרת במחלקה העל שלה.

עומס יתר של שיטה:
כאשר למחלקה יש מספר שיטות עם אותו שם אך פרמטרים שונים (מספר או סוגי ארגומנטים שונים).

במה משתמשים בנאים פרטיים?

–בנאים פרטיים משמשים במחלקות שירות או במחלקות קבועות.
מחלקות אלה מכילות רק שיטות סטטיות או איברים.

– הימנעות מסיווג משנה

— משלחת קונסטרוקטור:
בנאים פרטיים מאפשרים האצלת בנאים, כאשר בנאים ספציפיים קוראים לבנאי פרטי משותף.

— דפוס יחיד

על JVM

–הַהדָרָה:
כאשר אתה קומפילציה של קובץ .java, מהדר Java יוצר קבצי .class המכילים bytecode (ייצוג ביניים).
Bytecode אינו תלוי בפלטפורמה ויכול לפעול בכל מערכת עם JVM תואם.
–מערכת משנה של מטעין כיתה:
אחראי על טעינת שיעורים לזיכרון.
קורא קבצי .class, יוצר נתונים בינאריים ומאחסן אותם באזור השיטה.
יוצר אובייקט Class המייצג את המחלקה הטעונה בזיכרון הערימה.
דוגמה: Student s1 = new Student(); Class c1 = s1.getClass();
–מְקַשֵׁר:
מורכב משלושה שלבים:
אימות: מבטיח את נכונות קובץ ה-.class.
הכנה: מקצה זיכרון למשתני מחלקה ומאתחל אותם.
רזולוציה (אופציונלי): פותר הפניות סמליות למחלקות או שיטות אחרות.
–אִתחוּל:
מבצע אתחול סטטי (למשל, בלוקים סטטיים) ומאתחל משתנים סטטיים.
משתני מופע מאותחלים כאשר אובייקט נוצר.
–מנוע ביצוע:
ממיר קוד בתים לקוד מכונה.
כולל קומפילציה של Just-In-Time (JIT) לאופטימיזציה של ביצועים.

–לסיכום, ה-JVM מאפשר לתוכניות ג’אווה לרוץ בכל מקום על ידי פירוש בתים וניהול זיכרון

מהי השיטה הפרטית המופשטת?

–השתמש במקרים לשיטות פרטיות בשיעורים מופשטים:
בעוד ששיטות מופשטות אינן יכולות להיות פרטיות, אתה יכול להשתמש בשיטות פרטיות בתוך מחלקה מופשטת למטרות שונות:
–שיטות עוזר: יישם פונקציונליות משותפת המשותפת לשיטות אחרות.
–Encapsulation: הסתר פרטי יישום מתתי מחלקות.
–מחלקות דמויות מבנה: צור מחלקות קטנות עם שיטות פרטיות המשמשות כמכולות נתונים

מה זה אספן אשפה?

— זיכרון ערימה:
אובייקטי Java נוצרים בערימה, אזור זיכרון ייעודי.
אובייקטים מסוימים הופכים לבלתי ניתנים להשגה (התוכנית אינה מתייחסת עוד אליהם).
–אספן אשפה (GC):
ה-GC מזהה ומוחק אובייקטים שאינם בשימוש כדי לפנות זיכרון.
זה פועל אוטומטית ברקע.
שני סוגים:
מינור GC: מסיר חפצים בלתי ניתנים להשגה בדור הצעיר.
Major GC (Full GC): מנקה אובייקטים בדור הישן.
–יתרונות:
מפתחים אינם צריכים למחוק אובייקטים במפורש.
GC מבטיח זמינות זיכרון ומונע OutOfMemoryErrors

מה זה OOP?

ארבעה עקרונות של OOP:
–Encapsulation: צרור נתונים (תכונות) ושיטות (פונקציות) יחד בתוך מחלקה כדי להסתיר פרטי יישום ולספק ממשק נקי.
–ירושה: יצירת מחלקות חדשות המבוססות על מחלקות קיימות, בירושה של התכונות וההתנהגויות שלהן. זה מקדם שימוש חוזר בקוד.
— פולימורפיזם: מתן אפשרות לטפל באובייקטים ממעמדות שונים בצורה אחידה באמצעות ממשק משותף. זה מאפשר גמישות והרחבה.
– אבסטרקציה: התמקדות בתכונות חיוניות תוך הסתרת מורכבויות מיותרות. הפשטה מפשטת את העיצוב והופכת אותו לניהול יותר.

מהי ACID מאפיינים ממלאים תפקיד מכריע בהבטחת האמינות והעקביות של העסקאות.

–אָטוֹמִיוּת:
עסקאות מבוצעות במלואן או לא מבוצעות כלל.
אין ביצוע חלקי: אם עסקה נכשלת באמצע הדרך, כל השינויים שבוצעו במסד הנתונים יוחזרו לאחור.
דוגמה: שקול להעביר 100$ מחשבון X לחשבון Y. אם העסקה נכשלת לאחר ניכוי מ-X אך לפני הוספה ל-Y, מסד הנתונים נשאר עקבי.
–עֲקֵבִיוּת:
מסד הנתונים חייב לשמור על אילוצי תקינות לפני ואחרי עסקה.
דוגמה: אם הסכום הכולל לפני עסקה הוא $700 (X: $500, Y: $200), הוא אמור להישאר זהה לאחר העסקה (X: $400, Y: $300).
–בידוד:
עסקאות מרובות יכולות להתרחש במקביל מבלי להשפיע אחת על השנייה.
שינויים שנעשו על ידי עסקה אחת אינם גלויים לאחרים עד לביצוע.
מבטיח תוצאות שוות כאילו בוצעו עסקאות באופן סדרתי.
דוגמה: בידוד עסקאות מונע חוסר עקביות עקב פעולות משולבות.
–עֲמִידוּת:
לאחר ביצוע עסקה, השינויים בה הם קבועים.
שורד כשלי מערכת או קריסות.
מבטיח התמדה של נתונים.
דוגמה: גם לאחר הפעלה מחדש של המערכת, שינויים מחויבים נשארים ללא פגע.
זכור, מאפייני ACID מספקים את הבסיס לפעולות אמינות ועקביות של מסד נתונים! 🌟

מהו דפוס אסטרטגיה?
תיאור: מגדיר משפחה של אלגוריתמים, מקפל כל אחד והופך אותם לניתנים להחלפה. זה מאפשר לאלגוריתם להשתנות באופן עצמאי מלקוחות שמשתמשים בהם.
דוּגמָה:
Java

// ממשק להתנהגות אכילה
ממשק EatBehavior {
void eat();
}

// יישומים קונקרטיים של התנהגות אכילה
class NormalDiet מיישמת EatBehavior {
@עקוף
public void eat() {
// אוכל רגיל
}
}

class LosingWeightDiet מיישמת EatBehavior {
@עקוף
public void eat() {
// אוכל בריא
}
}

// שיעור חתול עם התנהגות אכילה
מחלקה מופשטת Cat {
EatBehavior eatBehavior;

public void eat() {
eatBehavior.eat();
}
public void setEatBehavior(EatBehavior eatBehavior) {
this.eatBehavior = eatBehavior;
}

}

// שימוש
חתול חתול = חתול חדש();
cat.setEatBehavior(new LosingWeightDiet());
cat.eat(); // חתול אוכל עם תזונה בריאה יותר

דפוסי יצירה:
דפוסים אלה מתמקדים באינסטציה בכיתה.
דוגמאות כלולות

— שיטת המפעל,
הצהרת בעיה: נניח שאנו רוצים ליצור סוגים שונים של מוצרים (למשל, מכוניות, אופניים, אופניים) מבלי לחבר את הקוד שלנו להטמעות ספציפיות.
מרכיבי מפתח:
מוצר: מחלקה או ממשק מופשטים המגדירים פעולות נפוצות עבור האובייקטים שהמפעל יצור.
מוצרים בטון: מחלקות בפועל המיישמות את ממשק המוצר, המייצגות סוגים ספציפיים של אובייקטים.
יוצר: מחלקה מופשטת או ממשק המצהיר על שיטת המפעל. הוא אחראי על יצירת אובייקטי מוצר אך מאציל את היצירה בפועל לתת-מחלקות.
Concrete Creators: תת מחלקות של הבורא המיישמות את שיטת המפעל. הם מחליטים איזה מוצר בטון ספציפי ליצור בהתבסס על פרמטרי קלט או תצורה.
שיטת מפעל: שיטה המוגדרת במחלקה Creator שיוצרת אובייקטים של מוצר. זה בדרך כלל מוכרז כמופשט אצל הבורא ומיושם אצל היוצרים הקונקרטיים.
דוגמה בג’אווה: נניח שיש לנו מפעל שמייצר סוגים שונים של רכבים. הנה דוגמה פשוטה:
Java

// מוצר מופשט
ממשק מוצר {
void display();
}

// מוצרי בטון
class Car מיישמת מוצר {
public void display() {
System.out.println(“נוצרה מכונית”);
}
}

class Bike מיישמת מוצר {
public void display() {
System.out.println(“אופניים נוצרו”);
}
}

// יוצר (מפעל)
ממשק מפעל {
Product factoryMethod();
}

// יוצרי בטון
class ConcreteFactoryA מיישמת את Factory {
public Product factoryMethod() {
החזר רכב חדש();
}
}

class ConcreteFactoryB מיישמת את Factory {
public Product factoryMethod() {
return new Bike();
}
}

מחלקה ציבורית FactoryMethodExample {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.factoryMethod();
productA.display();

    Factory factoryB = new ConcreteFactoryB();
    Product productB = factoryB.factoryMethod();
    productB.display();
}

}

— בונה
דפוס ה-Builder הוא דפוס עיצובי יצירתי המאפשר לבנות אובייקטים מורכבים צעד אחר צעד,
מפריד בין הבנייה שלהם לייצוג שלהם. הנה דוגמה פשוטה ב-Java:
// הגדר את המחלקה שברצונך לבנות
פוסט כיתה {
כותרת מחרוזת סופית פרטית;
טקסט מחרוזת סופית פרטית;
קטגוריית מחרוזת סופית פרטית;

Post(Builder builder) {
this.title = builder.title;
this.text = builder.text;
this.category = builder.category;
}
// Getters for properties
public String getTitle() {
return title;
}
public String getText() {
return text;
}
public String getCategory() {
return category;
}
// Builder class
public static class Builder {
private String title;
private String text;
private String category;
public Builder title(String title) {
this.title = title;
return this;
}
public Builder text(String text) {
this.text = text;
return this;
}
public Builder category(String category) {
this.category = category;
return this;
}
public Post build() {
return new Post(this);
}
}

}

// שימוש
כיתה ציבורית ראשי {
public static void main(String[] args) {
Post post = new Post.Builder()
.title(“דפוס בונה ג’אווה”)
.text(“הסבר כיצד ליישם את דפוס ה-Builder ב-Java”)
.category(“דפוסי עיצוב”)
.לִבנוֹת();

    System.out.println("Title: " + post.getTitle());
    System.out.println("Text: " + post.getText());
    System.out.println("Category: " + post.getCategory());
}

}

–אַב טִיפּוּס
תבנית אב הטיפוס היא תבנית עיצוב יצירתית המאפשרת לך ליצור אובייקטים חדשים על ידי העתקת אובייקט קיים.
במקום ליצור מופעים חדשים מאפס, אתה משכפל אב טיפוס קיים, שיכול להיות יעיל וגמיש יותר. בואו נצלול לדוגמא ב-Java:
הגדר את ממשק האב-טיפוס: ראשית, אנו יוצרים מחלקה מופשטת או ממשק בשם Tree עם עותק של שיטה מופשטת():
Java

כיתה תקציר ציבורית עץ {
public abstract Tree copy();
}

אבות טיפוס בטון: לאחר מכן, אנו מיישמים שתי מחלקות בטון שמרחיבות את עץ: עץ פלסטי ועץ אורן. מחלקות אלו מייצגות סוגים ספציפיים של עצים ומספקות יישום משלהן של שיטת copy():
Java

כיתה ציבורית PlasticTree מרחיב את העץ {
@עקוף
public Tree copy() {
PlasticTree plasticTreeClone = new PlasticTree(this.getMass(), this.getHeight());
plasticTreeClone.setPosition(this.getPosition());
להחזיר plasticTreeClone;
}
}

כיתה ציבורית PineTree מרחיבה את עץ {
@עקוף
public Tree copy() {
PineTree pineTreeClone = new PineTree(this.getMass(), this.getHeight());
pineTreeClone.setPosition(this.getPosition());
החזר pineTreeClone;
}
}
בשיעורים אלו, אנו מתאימים את תהליך השיבוט לפי סוג העץ הספציפי. לדוגמה, אנו מעתיקים את המסה, הגובה והמיקום של העץ המקורי.
שימוש: כעת, בואו ניצור מופעים של אבות הטיפוס הבטון שלנו:
Java

כיתה ציבורית ראשי {
public static void main(String[] args) {
Tree originalPlasticTree = new PlasticTree(10, 5);
Tree clonedPlasticTree = originalPlasticTree.copy();

    Tree originalPineTree = new PineTree(15, 8);
    Tree clonedPineTree = originalPineTree.copy();

    // Use the cloned trees as needed
    // ...
}

}

מהן דפוסים מבניים?
תבניות אלו עוסקות במבנה הכיתה ובהרכבה.

–דפוס מתאם
תבנית המתאם היא תבנית עיצוב מבנית המאפשרת לך לגרום לשני ממשקים לא תואמים לעבוד יחד.
הוא משמש כגשר בין ממשקים אלה, ומאפשר שיתוף פעולה מבלי לשנות את קוד המקור שלהם.

מתאם אובייקט:
בגישה זו, אנו משתמשים בהרכב כדי להאציל לוגיקה למתאם.
המתאם מכיל את Adaptee ומאציל את שיטת request() לשיטת specificRequest() ב-Adaptee.
Java

// Adaptee (מחלקה קיימת)
class Adaptee {
void specificeretRequest() {
System.out.println(“הבקשה הספציפית של Adaptee”);
}
}

// ממשק יעד
ממשק יעד {
void request();
}

// מתאם (באמצעות קומפוזיציה)
Class Adapter מיישם את Target {
מותאם סופי פרטי;

Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}

}

ClassAdapter:
דורש ירושה מרובה (לא אפשרי ישירות ב-Java).
אנו יכולים להשיג זאת באמצעות ממשקים עם שיטות ברירת מחדל.
המתאם מרחיב הן את שיעורי היעד והן את שיעורי ה-Adaptee.
Java

// ממשק יעד
ממשק יעד {
void request();
}

// Adaptee (מחלקה קיימת)
class Adaptee {
void specificeretRequest() {
System.out.println(“הבקשה הספציפית של Adaptee”);
}
}

// מתאם (באמצעות ירושה מחלקה)
class Adapter מרחיב Adaptee מיישם Target {
@עקוף
public void request() {
specificRequest();
}
}
הטבות והחלפות:
Class Adapter עובד היטב עבור מיפוי שיטה אחד לאחד.
מתאם אובייקטים מספק יותר גמישות ומונע שינוי קוד.

– דפוס דקורטור
דפוס הדקורטור הוא דפוס עיצובי יצירתי המאפשר לך להוסיף באופן דינמי פונקציונליות והתנהגות לאובייקט ללא
משפיע על התנהגותם של אובייקטים קיימים אחרים באותה מחלקה. זה שימושי במיוחד כאשר אתה רוצה להרחיב את זה של אובייקט
יכולות מבלי לשנות את המבנה שלו.

סקירה כללית:
דפוס הדקורטור מספק ממשק משופר לאובייקט המקורי על ידי עטיפתו בפונקציונליות נוספת.
אנו מעדיפים קומפוזיציה על פני ירושה כדי לצמצם תקורה של תת-סיווג.
ניתן להוסיף עיצובים בזמן ריצה, מה שמאפשר גמישות.
תרחיש דוגמה: נניח שיש לנו חפץ מעץ חג המולד, ואנו רוצים לקשט אותו בפריטים שונים כמו זר, טינסל ואורות בועות. אנו נעקוב אחר מוסכמות העיצוב המקוריות של Gang of Four:
יישום:
ראשית, צור ממשק ChristmasTree:
Java

ממשק ציבורי ChristmasTree {
מחרוזת לקשט();
}

הטמיע את הממשק עם עץ בסיסי:
Java

כיתה ציבורית ChristmasTreeImpl מיישמת את ChristmasTree {
@עקוף
public String decorate() {
החזרת “עץ חג המולד”;
}
}

לאחר מכן, צור כיתת TreeDecorator מופשטת המיישמת את ChristmasTree ומחזיקה את העץ המעוטר:
Java

כיתה מופשטת ציבורית TreeDecorator מיישמת את ChristmasTree {
עץ חג המולד פרטי אחרון;

public TreeDecorator(ChristmasTree tree) {
this.tree = tree;
}
@Override
public String decorate() {
return tree.decorate();
}

}

כעת צור מעצבים ספציפיים. לדוגמה, BubbleLights:
Java

כיתה ציבורית BubbleLights מרחיבה את TreeDecorator {
BubbleLights ציבורי (עץ חג המולד) {
סופר(עץ);
}

@Override
public String decorate() {
return super.decorate() + " with Bubble Lights";
}

}

גרלנד בכיתה ציבורית מרחיב את TreeDecorator {
זר ציבורי (עץ חג המולד) {
סופר(עץ);
}

@Override
public String decorate() {
return super.decorate() + " with Garland";
}

}
נוֹהָג:
צור עצים מעוצבים:
Java

כיתה ציבורית DecoratorPatternDemo {
public static void main(String[] args) {
ChristmasTree tree1 = new Garland(new ChristmasTreeImpl());
System.out.println(tree1.decorate()); // פלט: “עץ חג המולד עם גרלנד”

    ChristmasTree tree2 = new BubbleLights(new Garland(new Garland(new ChristmasTreeImpl())));
    System.out.println(tree2.decorate()); // Output: "Christmas tree with Garland with Garland with Bubble Lights"
}

}
הדוגמה מאפשרת הוספת מעצבים בזמן ריצה, ומעניקה גמישות לקישוט לפי הצורך.
זכור, דפוס הדקורטור נמצא בשימוש נרחב בתרחישים שבהם אתה רוצה לשפר את התנהגותו של אובייקט מבלי לשנות את מבנה הליבה שלו

— דפוס חזית
דפוס החזית הוא דפוס עיצוב מבני המפשט את האינטראקציה עם תת-מערכת מורכבת על ידי מתן ממשק אחיד.
היא פועלת כ”דלת כניסה” כדי להסתיר את המורכבות הבסיסית ולהקל על הלקוחות להשתמש במערכת

–מוּרכָּב
התבנית המרוכבת היא תבנית עיצוב מבנית המאפשרת לך לחבר אובייקטים למבני עצים כדי לייצג היררכיות חלקיות שלמות.
הרעיון המרכזי מאחורי התבנית המרוכבת הוא לבנות מבנה עץ של אובייקטים, שבו אובייקטים בודדים ואובייקטים מרוכבים חולקים ממשק משותף.

רכיב בסיס (מחלקה): נגדיר ממשק מחלקה פשוט כרכיב הבסיס שלנו:
Java

מחלקת ממשק ציבורי {
void printDepartmentName();
}

רכיבי עלה (מחלקה פיננסית ומחלקת מכירות): בואו ניצור שיעורים למחלקות פיננסיות ומכירות, שניהם מיישמים את ממשק המחלקה:
Java

מחלקה ציבורית המחלקה הפיננסית מיישמת את המחלקה {
// פרטי יישום (למשל, מזהה, שם)
public void printDepartmentName() {
System.out.println(getClass().getSimpleName());
}
}

מחלקה ציבורית מחלקת המכירות מיישמת את המחלקה {
// פרטי יישום (למשל, מזהה, שם)
public void printDepartmentName() {
System.out.println(getClass().getSimpleName());
}
}

מחלקות עלים אלה מיישמות את השיטה printDepartmentName() ואינן מכילות הפניות לאובייקטים אחרים של Department.
רכיב מורכב (HeadDepartment): כמחלקה מורכבת, בואו ניצור HeadDepartment שמחזיקה אוסף של רכיבי מחלקה:
Java

ייבוא ​​java.util.ArrayList;
ייבוא ​​java.util.List;

מחלקה ציבורית HeadDepartment מיישם את המחלקה {
מזהה מספר שלם פרטי;
שם מחרוזת פרטי;
רשימת ילדים פרטית;

public HeadDepartment(Integer id, String name) {
this.id = id;
this.name = name;
this.childDepartments = new ArrayList<>();
}
public void printDepartmentName() {
childDepartments.forEach(Department::printDepartmentName);
}
public void addDepartment(Department department) {
childDepartments.add(department);
}
public void removeDepartment(Department department) {
childDepartments.remove(department);
}

}

המחלקה HeadDepartment מיישמת את שיטת printDepartmentName() ומספקת שיטות להוספה והסרה של מחלקות צאצא.
שימוש: אתה יכול ליצור מופעים של מחלקות עלים (למשל, FinancialDepartment, SalesDepartment) ומחלקה מורכבת (למשל, HeadDepartment). הוסף מחלקות עלים למכלול והדפיס את שמותיהן.
Java

כיתה ציבורית ראשי {
public static void main(String[] args) {
מחלקה פיננסית = new FinancialDepartment();
מכירות מחלקה = New SalesDepartment();

    HeadDepartment headDepartment = new HeadDepartment(1, "Company Departments");
    headDepartment.addDepartment(finance);
    headDepartment.addDepartment(sales);

    headDepartment.printDepartmentName();
}

}

תְפוּקָה:
המחלקה הפיננסית
מחלקת מכירות

התבנית המרוכבת מאפשרת לך ליצור מבני עץ גמישים היכן
ניתן להתייחס באופן אחיד לחפצים וקומפוזיציות בודדות (מרוכבים).

מהי פונקציית ה-HASH!!!

פונקציית hash היא פונקציה מתמטית שלוקחת קלט (או “הודעה”) ומחזירה מחרוזת תווים בגודל קבוע,
שהוא בדרך כלל רצף של מספרים ואותיות. פלט זה נקרא “ערך hash” או פשוט “hash”.
פונקציות Hash נמצאות בשימוש נפוץ במדעי המחשב ובקריפטוגרפיה, ויש להן כמה מאפיינים חשובים:

=== איך הייתם משתמשים בפונקציית ה-hash ואיך היא פועלת?

דטרמיניסטי: אותו קלט תמיד יפיק את אותו hash.

מהיר: הפונקציה יכולה לחשב במהירות את ערך הגיבוב עבור כל קלט נתון.
ייחודי (כמעט): שתי כניסות שונות אמורות לייצר גיבוב (hash) ברור (זה ידוע כמזעור “התנגשויות”).
בלתי הפיך: בהינתן hash, זה אמור להיות בלתי אפשרי מבחינה חישובית לקבוע את הקלט המקורי.
אחיד: ערכי ה-hash מחולקים באופן שווה על פני שטח הפלט.

פונקציות Hash נמצאות בשימוש נרחב ביישומים שונים, כגון:

שלמות נתונים: כדי להבטיח שהנתונים לא שונו או מזוייפים, על ידי השוואת ערכי hash.
אחסון סיסמאות: אחסון מאובטח של סיסמאות גיבוב במקום הסיסמאות בפועל.
קריפטוגרפיה: משמש בחתימות דיגיטליות ואלגוריתמים קריפטוגרפיים אחרים.
טבלאות Hash: לאחזור נתונים יעיל בתכנות.

=== תן לי דוגמה למפת hash, איך היית משתמש בה?

SHA (Secure Hash Algorithm):
כולל גרסאות כמו SHA-1, SHA-256, SHA-384 ו-SHA-512.
SHA-256 ו-SHA-512 נמצאים בשימוש נרחב בטכנולוגיית קריפטוגרפיה ובלוקצ’יין (למשל, ביטקוין).

ייבוא ​​java.security.MessageDigest;
ייבוא ​​java.security.NoSuchAlgorithmException;

מחלקה ציבורית HashExample {
public static void main(String[] args) {
נסה {
// מחרוזת הקלט שיש לגיבוב
קלט מחרוזת = “שלום, עולם!”;

        // Create a MessageDigest instance for SHA-256
        MessageDigest digest = MessageDigest.getInstance("SHA-256");

        // Compute the hash
        byte[] hash = digest.digest(input.getBytes());

        // Convert the hash bytes to a hexadecimal string
        StringBuilder hexString = new StringBuilder();
        for (byte b : hash) {
            String hex = String.format("%02x", b);
            hexString.append(hex);
        }

        // Print the resulting hash
        System.out.println("SHA-256 Hash: " + hexString.toString());
    } catch (NoSuchAlgorithmException e) {
        System.err.println("Error: " + e.getMessage());
    }
}

}

נתוני קלט: משתנה הקלט מכיל את הנתונים שברצונך לבצע hash.
אתחול MessageDigest: המופע של MessageDigest נוצר באמצעות getInstance(“SHA-256”), המציין את אלגוריתם הגיבוב SHA-256.
גיבוב: שיטת התקציר מחשבת את ה-hash של נתוני הקלט.
המרה הקסדצימלית: מכיוון שה-hash הוא בפורמט בינארי, אנו משתמשים בלולאה כדי להמיר אותו למחרוזת הקסדצימלית קריאה יותר.

לשים ותיקון? מתי להשתמש בו?

הן PUT והן PATCH הן שיטות HTTP המשמשות לעדכון משאבים בשרת, אך הן שונות באופן שבו הן מטפלות בעדכונים:

לָשִׂים:
עדכון מלא:
PUT משמש להחלפה מלאה של משאב קיים.
יש לשלוח את המשאב כולו בבקשה, גם אם רק חלק ממנו משתנה.

אם המשאב אינו קיים, PUT יוצר אותו לעתים קרובות (אם כי התנהגות זו עשויה להשתנות).

אימפוטנטי:

בקשות PUT חוזרות עם אותם נתונים תמיד יגרמו לאותו מצב של המשאב.
דוגמה: עדכון פרופיל משתמש:

json
PUT /users/123
{
“name”: “וליסלב”,
“email”: OZYX2Zvelislav@example.comOZYX3Z,
“גיל”: 30
}
אם רק השם משתנה אבל המייל והגיל נשארים זהים, עדיין יש לכלול אותם בבקשה.

תיקונים:
עדכון חלקי:

PATCH משמש לעדכון חלקי למשאב קיים.
יש לשלוח בבקשה רק את השדות המשתנים.

אימפוטנטי (בעיקר):

כמו PUT, בקשות PATCH חוזרות עם אותם נתונים מביאות בדרך כלל לאותו מצב של המשאב.
עם זאת, התנהגות עשויה להיות תלויה ביישום השרת.

דוגמה: עדכון שם המשתמש בלבד:

json
PATCH /users/123
{
“name”: “וליסלב”
}
כאן, רק שדה השם מתעדכן, ומשאירים שדות אחרים ללא שינוי.

ההבדל העיקרי:
PUT מחליף את המשאב כולו, בעוד PATCH משנה רק שדות שצוינו של המשאב.

שיטות HTTP עם תיאור קצר.

GET: משמש לאחזור נתונים מהשרת מבלי לשנות אותם. משמש בדרך כלל לאחזור משאבים או מידע.
POST: משמש לשליחת נתונים לשרת, בדרך כלל ליצירת משאב חדש. למשל, שליחת טופס או העלאת קובץ.
PUT: משמש לעדכון או החלפה מלאה של משאב קיים בשרת. אם המשאב אינו קיים, ייתכן שהוא נוצר.
PATCH: משמש לעדכונים חלקיים למשאב קיים, תוך שינוי רק בשדות שצוינו.
מחק: משמש למחיקת משאב מהשרת.
HEAD: בדומה ל-GET, משמש לאחזור כותרות ללא גוף של משאב.
אפשרויות: זה אומר אילו שיטות HTTP מותרות.
TRACE: משמש בעיקר למטרות אבחון על ידי הדהוד של הבקשה חזרה ללקוח
CONNECT: יוצר מנהרה לשרת, המשמשת לעתים קרובות לחיבורי SSL/TLS.

========== ממשק פונקציונלי ===================================

ממשק פונקציונלי ב-Java הוא ממשק המכיל בדיוק שיטה מופשטת אחת. ממשקים אלה משמשים בעיקר בביטויי למבדה והפניות לשיטות, המאפשרים תכנות פונקציונלי תמציתי וקריא יותר.

תכונות עיקריות:

  1. שיטה מופשטת אחת: לממשק פונקציונלי יש רק שיטה מופשטת אחת. זוהי המהות של הפונקציונליות שלו.
  2. שיטות ברירת מחדל וסטטיות: ממשקים פונקציונליים יכולים לכלול שיטות ברירת מחדל ושיטות סטטיות, אך אלו אינן נחשבות כמתודה מופשטת יחידה.
  3. הערת @FunctionalInterface: אמנם הערה זו היא אופציונלית, אך היא עוזרת בכך שהיא מסמנת שהממשק נועד להיות פונקציונלי. אם מתווספות שיטות מופשטות נוספות לממשק, המהדר זורק שגיאה.

דוּגמָה:

@FunctionalInterface
interface Calculator {
int calculate(int a, int b);
}
// Using lambda expression
public class Main {
public static void main(String[] args) {
// Implementing the calculate method using a lambda
Calculator addition = (a, b) -> a + b;
Calculator multiplication = (a, b) -> a * b;
System.out.println("Addition: " + addition.calculate(5, 3)); // Output: 8
System.out.println("Multiplication: " + multiplication.calculate(5, 3)); // Output: 15
}
}

מקרה שימוש:

ה Calculator לממשק בדוגמה יש שיטה מופשטת אחת calculate, מה שהופך אותו לממשק פונקציונלי.
זה מאפשר לנו להשתמש בביטויי למבדה לקיצור ואלגנטיות ביישום.

ממשקים פונקציונליים נפוצים ב-Java:

Java מספקת מספר ממשקים פונקציונליים מובנים ב- java.util.function חבילות, כגון:

  • לְבַסֵס: לוקח קלט ומחזיר ערך בוליאני.
  • פוּנקצִיָה: לוקח קלט ומחזיר פלט מסוג R.
  • סַפָּק: מספק ערך ללא קלט.
  • צַרכָן: מקבל ערך ומבצע פעולה.

ממשקים פונקציונליים מייעלים את הפעולות, במיוחד בתרחישים כמו עבודה עם אוספים, זרמים או משימות אסינכרוניות.

הם מעצימים את ג’אווה לאמץ פרדיגמות תכנות פונקציונליות תוך שמירה על אופייה מונחה עצמים.

================= פונקציה מסדר גבוה יותר היא =============================
פונקציה מסדר גבוה היא פונקציה שלוקחת פונקציות אחרות כארגומנטים או מחזירה פונקציה כתוצאה ממנה. במילים אחרות, היא פועלת על פונקציות, ומתייחסת אליהן כאל “אזרחים מהשורה הראשונה”. זוהי תכונה רבת עוצמה בפרדיגמות תכנות פונקציונליות, המאפשרת הפשטה, שימוש חוזר בקוד וקומפוזיציה.

דוגמה לפונקציה מסדר גבוה ב-Java:

import java.util.function.Function;
public class Main {
public static void main(String[] args) {
// Higher-order function: applies a function to a value
Function<Integer, Integer> squareFunction = x -> x * x;
int result = applyFunction(5, squareFunction);
System.out.println("Square of 5: " + result); // Output: 25
}
public static int applyFunction(int value, Function<Integer, Integer> function) {
return function.apply(value);
}
}

בדוגמה למעלה:

  • ה applyFunction השיטה היא פונקציה מסדר גבוה יותר מכיוון שהיא מקבלת פונקציה אחרת (Function<Integer, Integer>) כפרמטר.

תכונות:

  1. מקבל פונקציות כטיעונים: פונקציות מסדר גבוה יותר יכולות לקחת פונקציות אחרות כקלט לביצוע פעולות ספציפיות.
  2. מחזיר פונקציות: הם יכולים ליצור ולהחזיר פונקציות חדשות באופן דינמי.
  3. מעודד שימוש חוזר בקוד: מקדם קוד מודולרי וניתן לשימוש חוזר מכיוון שניתן להעביר פונקציות ולהרכיב אותן יחד.

מקרה שימוש:

ב-Java, פונקציות מסדר גבוה יותר משמשות בדרך כלל בתכנות פונקציונלי עם זרמים, למבדות והפניות לשיטות. לְדוּגמָה:

List<String> names = Arrays.asList("John", "Jane", "Jake");
names.stream()
.filter(name -> name.startsWith("J")) // Takes a lambda function as argument
.forEach(System.out::println); // Takes a method reference as argument

פונקציות מסדר גבוה מביאות גמישות ועוצמה לתוכניות Java, ומאפשרות למפתחים לכתוב קוד נקי, תמציתי ואקספרסיבי!

כמה תגובת ראיון בעולם האמיתי מופיעה כאן.

  • מבין יסודות כמו פולימורפיזם, שיטות סטטיות ומאגר מיתרים
  • מכיר אוספים (למשל, HashSet, TreeSet) ומקרי השימוש בהם.
  • מזהה נכון טווחי שעועית (יחיד, אב טיפוס)
  • מודע להזרקת תלות ומטרתה.
  • יודע ליישם יחסים רבים לרבים באמצעות טבלאות צומת.
  • מבין את מטרת האינדקסים (אם כי באופן שטחי)
  • יכול להבדיל בין ארכיטקטורות מונוליטיות למיקרו-שירותים
  • מבין פשרות בסיסיות (מדרגיות, תחזוקה)
  • לא מבין את עקרון ההחלפה של Liskov והיפוך התלות
  • נאבקים לספק דוגמאות קונקרטיות לעקרון הפתוח/סגור.
  • חסר עומק בניהול מחזור החיים של שעועית
  • לא ברור לגבי הזרקת תלות מתקדמת
  • הבנה שטחית של אינדקסים (אינדקסים מורכבים, אופטימיזציה של שאילתות)
  • לא מכיר רמות בידוד ונעילה אופטימית/פסימית

יתרונות:

  • עבד עם קוד מדור קודם במערכת ענקית ומורכבת
  • יודע SOLID
  • הכרת זרמים
  • אינדקסים ו-ACID מוסברים
  • אנגלית ברמה טובה
    חסרונות:
  • אין אחריות על פריסה או תשתית, השתתפות בתכנון אדריכלות
  • יש לו קצת ידע בעקרונות ה-OOP אבל ממש התקשה להסביר אותם וטעה כמה מושגים
  • ידע כללי באוספים אך בעל פערים ומאוד לא ודאי
  • לא מבין את ההבדל בין מקביליות למקביליות, לא ידע חזק במיוחד של מקביליות
  • לא בחנו הרבה את ארכיטקטורת השירותים המיקרוניים
  • מעורפל לגבי כמה מושגי מסד נתונים (הדיון באינדקסים היה ברמת פני השטח)
  • לא הצלחתי להסביר את livelock
  • פערים חוסמים סוף סוף את ההבנה
  • ידע מוגבל בפרטי טיפול בחריגים
  • מבולבל לגבי משנה גישה (מוגן לעומת חבילה פרטית)
    דעת הקונצנזוס… היא
    אינו מתאים לעמדת מפתח Mid-Java עקב הפערים בידע הטכני שלו.

יתרונות
הסבר מוצק היטב
הסביר טוב OOP
הרכב מוסבר (אבל התקשה קצת בהתחלה)
הבנה מצוינת באוספים
הבדל בין רשימות וסטים
ערכות חשיש
Hashmap
ConcurrentHashmap
הבנה טובה של מבני נתונים – ערימות, תורים, עצים
ידע טוב בארכיטקטורת jvm
הסבר מקביל לעומת מקביליות, הוכיח ידע טוב ביישומים עם ריבוי הליכי
הבנה טובה של REST
הסביר היטב את מדדי db
הסבר על סוגים שונים של חיבורים

השאר תגובה

גלה עוד מהבלוג של Velislav Krastev

הירשם עכשיו כדי להמשיך לקרוא ולקבל גישה לארכיון המלא.

המשך לקרוא