diff --git a/src/Calc/AddOperation.java b/src/Calc/AddOperation.java new file mode 100644 index 0000000..da08480 --- /dev/null +++ b/src/Calc/AddOperation.java @@ -0,0 +1,16 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package Calc; + +/** + * + * @author Sulta + */ +public class AddOperation implements Operation{ + @Override + public float apply(float a, float b) { + return a + b; + } +} diff --git a/src/Calc/App.java b/src/Calc/App.java index 4101058..f658618 100644 --- a/src/Calc/App.java +++ b/src/Calc/App.java @@ -1,14 +1,19 @@ package Calc; -/** - * - * @author youcefhmd - */ - public class App { - public static void main(String[] args) { - new Calculator().setVisible(true); - } + javax.swing.SwingUtilities.invokeLater(() -> { + + CalculatorFacade calcFacade = new CalculatorFacade(); + // بدل ما نستدعي Calculator مباشرة: + calcFacade.showCalculator(); + + // مثال تشغيل: + float result = calcFacade.performOperation("+", 5, 3); + System.out.println("Result = " + result); + }); + } } + +// \ No newline at end of file diff --git a/src/Calc/Calculator.java b/src/Calc/Calculator.java index 7e2a37c..7839983 100644 --- a/src/Calc/Calculator.java +++ b/src/Calc/Calculator.java @@ -3,6 +3,16 @@ import java.awt.Color; import java.awt.event.*; import javax.swing.JButton; +import Calc.Operation; +import Calc.AddOperation; +import Calc.SubOperation; +import Calc.MultOperation; +import Calc.DivOperation; +import Calc.OperationFactory; +import Calc.OperationDecorator; +import Calc.HistoryOperation; +import Calc.LoggingOperation; + /** * @@ -13,16 +23,109 @@ public final class Calculator extends javax.swing.JFrame { private String currentOperand; private String previousOperand; private String operation; - private int x, y; + + // --- Singleton Method --- + private static Calculator INSTANCE; - public Calculator() { - initComponents(); - getContentPane().setSize(400, 700); - this.clear(); - this.addEvents(); + public static Calculator getInstance() { + if (INSTANCE == null) { + INSTANCE = new Calculator(); + } + return INSTANCE; } + + + + + + + // -------------------- State Pattern -------------------- +private CalculatorState state = new IdleState(); + +public void setState(CalculatorState newState) { + this.state = newState; +} + +// دوال هندل (واجهة واضحة تُنادى من الأزرار) +public void handleNumber(String digit) { + state.onNumber(this, digit); +} + +public void handleOperation(String op) { + state.onOperation(this, op); +} + +public void handleEquals() { + state.onEquals(this); +} + +public void handleClear() { + state.onClear(this); +} +// --------------------------------------------------------- + + + + + + + + + + + + +private Calculator() { + initComponents(); + getContentPane().setSize(400, 700); + this.clear(); + this.addEvents(); +} + + + + + + + +// --- Helper Methods used by States --- + +public void setDisplay(String value) { + this.currentOperand = value; + updateDisplay(); +} + +public void appendDigit(String digit) { + this.currentOperand += digit; + updateDisplay(); +} + +public void clearDisplay() { + this.currentOperand = ""; + this.previousOperand = ""; + updateDisplay(); +} + +public void saveFirstOperand() { + this.previousOperand = this.currentOperand; +} + +public void setOperation(String op) { + this.operation = op; + updateDisplay(); +} +// ---------------- END STATE PATTERN --------------- + + + + + + + + + public void addEvents() { JButton[] btns = { btn0, btn1, btn2, btn3, btn4, @@ -35,12 +138,27 @@ public void addEvents() { btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9 }; + + + + + + + + +for (JButton number : numbers) { + number.addActionListener((ActionEvent e) -> { + handleNumber(((JButton) e.getSource()).getText()); + }); +} + + + + + + + - for (JButton number : numbers) { - number.addActionListener((ActionEvent e) -> { - appendNumber(((JButton) e.getSource()).getText()); - }); - } for (JButton btn : btns) { btn.addMouseListener(new MouseAdapter() { @@ -61,7 +179,7 @@ public void mouseExited(MouseEvent e) { } }); } - } + } public void clear() { this.currentOperand = ""; @@ -108,43 +226,40 @@ public void chooseOperation(String operation) { this.updateDisplay(); } - public void compute() { - float computation; - if (this.currentOperand.equals("") || this.previousOperand.equals("")) { - return; - } +public void compute() { + + if (this.currentOperand.equals("") || this.previousOperand.equals("")) { + return; + } + try { float curr = Float.parseFloat(this.currentOperand); float prev = Float.parseFloat(this.previousOperand); - if (Float.isNaN(curr) || Float.isNaN(prev)) { - return; - } - switch (this.operation) { - case "+" -> - computation = prev + curr; - case "-" -> - computation = prev - curr; - case "×" -> - computation = prev * curr; - case "÷" -> { - if (curr == 0) { - this.clear(); - this.currentOperand = "Error"; - return; - } - computation = prev / curr; - } - default -> { - return; - } - } + // نجيب العملية الأساسية من الفاكتوري + Operation op = OperationFactory.getOperation(this.operation); - this.currentOperand = (computation - (int) computation) != 0 ? Float.toString(computation) : Integer.toString((int) computation); - this.previousOperand = ""; - this.operation = ""; + // نغلفها بـ History + Logging (Decorator Pattern) + op = new HistoryOperation(op); + op = new LoggingOperation(op); + + float result = op.apply(prev, curr); + + this.currentOperand = (result - (int) result) != 0 + ? Float.toString(result ) + : Integer.toString((int) result ); + + } catch (IllegalArgumentException | ArithmeticException ex) { + this.clear(); + this.currentOperand = "Error"; + return; } + this.previousOperand = ""; + this.operation = ""; +} + + public void updateDisplay() { current.setText(this.currentOperand); previous.setText(previousOperand + " " + this.operation); @@ -581,7 +696,7 @@ private void btnDotActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST: }//GEN-LAST:event_btnDotActionPerformed private void btnClearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnClearActionPerformed - clear(); + handleClear(); }//GEN-LAST:event_btnClearActionPerformed private void btnDelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDelActionPerformed @@ -592,24 +707,40 @@ private void btnDelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST: }//GEN-LAST:event_btnDelActionPerformed private void btnPlusActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnPlusActionPerformed - chooseOperation("+"); + handleOperation("+"); }//GEN-LAST:event_btnPlusActionPerformed private void btnMultActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMultActionPerformed - chooseOperation("×"); + handleOperation("×"); }//GEN-LAST:event_btnMultActionPerformed private void btnSubActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSubActionPerformed - chooseOperation("-"); + handleOperation("-"); }//GEN-LAST:event_btnSubActionPerformed private void btnDivActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDivActionPerformed - chooseOperation("÷"); + handleOperation("÷"); }//GEN-LAST:event_btnDivActionPerformed private void btnEqualActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnEqualActionPerformed - this.compute(); - this.updateDisplay(); +handleEquals(); + + + + + System.out.println("---- History ----"); +for (String h : HistoryOperation.getHistory()) { + System.out.println(h); +} + + + + + + + + + if (this.currentOperand.equals("Error")) this.currentOperand = ""; }//GEN-LAST:event_btnEqualActionPerformed diff --git a/src/Calc/CalculatorFacade.java b/src/Calc/CalculatorFacade.java new file mode 100644 index 0000000..a43218c --- /dev/null +++ b/src/Calc/CalculatorFacade.java @@ -0,0 +1,39 @@ +package Calc; + + +import Calc.Operation; +import Calc.OperationFactory; +import java.util.List; +import Calc.HistoryOperation; +public class CalculatorFacade { + + private Calculator calculator; + + public CalculatorFacade() { + calculator = Calculator.getInstance(); // Singleton + } + + // تفتح واجهة الآلة الحاسبة + public void showCalculator() { + calculator.setVisible(true); + } + + // تنفّذ عملية رياضية وتُرجع الناتج + public float performOperation(String operation, float a, float b) { + Operation op = OperationFactory.getOperation(operation); + if (op == null) { + throw new IllegalArgumentException("Invalid operation: " + operation); + } + return op.apply(a, b); + } + + // تمسح كل شي من الشاشة (Clear) + public void clearCalculator() { + calculator.clear(); + } + + // تعرض الـ History (من Decorator) + public java.util.List getHistory() { + return HistoryOperation.getHistory(); + } +} diff --git a/src/Calc/CalculatorState.java b/src/Calc/CalculatorState.java new file mode 100644 index 0000000..05bb85f --- /dev/null +++ b/src/Calc/CalculatorState.java @@ -0,0 +1,16 @@ +package Calc; + +/** + * واجهة تمثل حالة الآلة الحاسبة. + * كل حالة تحدد كيف تتصرف الآلة عند ضغط رقم / عملية / يساوي / مسح. + */ +public interface CalculatorState { + + void onNumber(Calculator calc, String digit); + + void onOperation(Calculator calc, String op); + + void onEquals(Calculator calc); + + void onClear(Calculator calc); +} diff --git a/src/Calc/DivOperation.java b/src/Calc/DivOperation.java new file mode 100644 index 0000000..7bdca7f --- /dev/null +++ b/src/Calc/DivOperation.java @@ -0,0 +1,19 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package Calc; + +/** + * + * @author Sulta + */ +public class DivOperation implements Operation{ + @Override + public float apply(float a, float b) { + if (b == 0f) { + throw new ArithmeticException("Error! Division by zero is not accepted"); + } + return a / b; + } +} diff --git a/src/Calc/HistoryOperation.java b/src/Calc/HistoryOperation.java new file mode 100644 index 0000000..0a800c4 --- /dev/null +++ b/src/Calc/HistoryOperation.java @@ -0,0 +1,34 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package Calc; + +import java.util.ArrayList; +import java.util.List; +public class HistoryOperation extends OperationDecorator{ + private static final List history = new ArrayList<>(); + + public HistoryOperation(Operation decoratedOperation) { + super(decoratedOperation); + } + + @Override + public float apply(float a, float b) { + float result = super.apply(a, b); + history.add(a + " " + getSymbol(decoratedOperation) + " " + b + " = " + result); + return result; + } + + private String getSymbol(Operation op) { + if (op instanceof AddOperation) return "+"; + if (op instanceof SubOperation) return "-"; + if (op instanceof MultOperation) return "×"; + if (op instanceof DivOperation) return "÷"; + return "?"; + } + + public static List getHistory() { + return history; + } +} diff --git a/src/Calc/IdleState.java b/src/Calc/IdleState.java new file mode 100644 index 0000000..6062c41 --- /dev/null +++ b/src/Calc/IdleState.java @@ -0,0 +1,26 @@ +package Calc; + +public class IdleState implements CalculatorState { + + @Override + public void onNumber(Calculator calc, String digit) { + calc.setDisplay(digit); + calc.setState(new TypingState()); + } + + @Override + public void onOperation(Calculator calc, String op) { + // لا يمكن اختيار عملية قبل كتابه اول رقم + } + + @Override + public void onEquals(Calculator calc) { + // ولا شي + } + + @Override + public void onClear(Calculator calc) { + calc.clear(); + calc.setState(new IdleState()); + } +} diff --git a/src/Calc/LoggingOperation.java b/src/Calc/LoggingOperation.java new file mode 100644 index 0000000..f0b3e0c --- /dev/null +++ b/src/Calc/LoggingOperation.java @@ -0,0 +1,30 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package Calc; + +/** + * + * @author Sulta + */ +public class LoggingOperation extends OperationDecorator{ + public LoggingOperation(Operation decoratedOperation) { + super(decoratedOperation); + } + + @Override + public float apply(float a, float b) { + float result = super.apply(a, b); + System.out.println("[LOG] " + a + " " + getSymbol(decoratedOperation) + " " + b + " = " + result); + return result; + } + + private String getSymbol(Operation op) { + if (op instanceof AddOperation) return "+"; + if (op instanceof SubOperation) return "-"; + if (op instanceof MultOperation) return "×"; + if (op instanceof DivOperation) return "÷"; + return "?"; + } +} diff --git a/src/Calc/MultOperation.java b/src/Calc/MultOperation.java new file mode 100644 index 0000000..158dfea --- /dev/null +++ b/src/Calc/MultOperation.java @@ -0,0 +1,16 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package Calc; + +/** + * + * @author Sulta + */ +public class MultOperation implements Operation{ + @Override + public float apply(float a, float b) { + return a * b; + } +} diff --git a/src/Calc/Operation.java b/src/Calc/Operation.java new file mode 100644 index 0000000..5d28fef --- /dev/null +++ b/src/Calc/Operation.java @@ -0,0 +1,13 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Interface.java to edit this template + */ +package Calc; + +/** + * + * @author Sulta + */ +public interface Operation { + float apply(float a, float b); +} diff --git a/src/Calc/OperationDecorator.java b/src/Calc/OperationDecorator.java new file mode 100644 index 0000000..49ee4f3 --- /dev/null +++ b/src/Calc/OperationDecorator.java @@ -0,0 +1,22 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package Calc; + +/** + * + * @author Sulta + */ +public class OperationDecorator implements Operation{ + protected Operation decoratedOperation; + + public OperationDecorator(Operation decoratedOperation) { + this.decoratedOperation = decoratedOperation; + } + + @Override + public float apply(float a, float b) { + return decoratedOperation.apply(a, b); + } +} diff --git a/src/Calc/OperationFactory.java b/src/Calc/OperationFactory.java new file mode 100644 index 0000000..6934a5d --- /dev/null +++ b/src/Calc/OperationFactory.java @@ -0,0 +1,26 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package Calc; + +/** + * + * @author Sulta + */ +public class OperationFactory { + public static Operation getOperation(String op) { + switch (op) { + case "+": + return new AddOperation(); + case "-": + return new SubOperation(); + case "×": + return new MultOperation(); + case "÷": + return new DivOperation(); + default: + throw new IllegalArgumentException("Unknown operation: " + op); + } + } +} diff --git a/src/Calc/OperationSelectedState.java b/src/Calc/OperationSelectedState.java new file mode 100644 index 0000000..c9639fd --- /dev/null +++ b/src/Calc/OperationSelectedState.java @@ -0,0 +1,26 @@ +package Calc; + +public class OperationSelectedState implements CalculatorState { + + @Override + public void onNumber(Calculator calc, String digit) { + calc.setDisplay(digit); + calc.setState(new TypingState()); + } + + @Override + public void onOperation(Calculator calc, String op) { + calc.setOperation(op); + } + + @Override + public void onEquals(Calculator calc) { + // لا يوجد رقم ثاني → تجاهل فقط + } + + @Override + public void onClear(Calculator calc) { + calc.clear(); + calc.setState(new IdleState()); + } +} diff --git a/src/Calc/ResultState.java b/src/Calc/ResultState.java new file mode 100644 index 0000000..0cb289e --- /dev/null +++ b/src/Calc/ResultState.java @@ -0,0 +1,28 @@ +package Calc; + +public class ResultState implements CalculatorState { + + @Override + public void onNumber(Calculator calc, String digit) { + calc.setDisplay(digit); + calc.setState(new TypingState()); + } + + @Override + public void onOperation(Calculator calc, String op) { + calc.saveFirstOperand(); + calc.setOperation(op); + calc.setState(new OperationSelectedState()); + } + + @Override + public void onEquals(Calculator calc) { + // تجاهل + } + + @Override + public void onClear(Calculator calc) { + calc.clear(); + calc.setState(new IdleState()); + } +} diff --git a/src/Calc/SubOperation.java b/src/Calc/SubOperation.java new file mode 100644 index 0000000..aeee3b9 --- /dev/null +++ b/src/Calc/SubOperation.java @@ -0,0 +1,16 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package Calc; + +/** + * + * @author Sulta + */ +public class SubOperation implements Operation{ + @Override + public float apply(float a, float b) { + return a - b; + } +} diff --git a/src/Calc/TypingState.java b/src/Calc/TypingState.java new file mode 100644 index 0000000..921c2f5 --- /dev/null +++ b/src/Calc/TypingState.java @@ -0,0 +1,29 @@ +package Calc; + +public class TypingState implements CalculatorState { + + @Override + public void onNumber(Calculator calc, String digit) { + calc.appendDigit(digit); + } + + @Override + public void onOperation(Calculator calc, String op) { + calc.saveFirstOperand(); + calc.setOperation(op); + calc.setState(new OperationSelectedState()); + } + + @Override + public void onEquals(Calculator calc) { + calc.compute(); + calc.updateDisplay(); + calc.setState(new ResultState()); + } + + @Override + public void onClear(Calculator calc) { + calc.clear(); + calc.setState(new IdleState()); + } +}