diff --git a/dart_ex_1/etc.md b/dart_ex_1/etc.md index e69de29..a017eeb 100644 --- a/dart_ex_1/etc.md +++ b/dart_ex_1/etc.md @@ -0,0 +1,9 @@ +return SmartLight(id: id, name: name); +--- +This is poor code naming. +you should use naming like this. +if user provides id and name: user_provided_id, user_provided_name +So I know the relationship. + + + diff --git a/dart_ex_1/ex_2.dart b/dart_ex_1/ex_2.dart index 3a2406c..0a387e6 100644 --- a/dart_ex_1/ex_2.dart +++ b/dart_ex_1/ex_2.dart @@ -1,114 +1,192 @@ +// WHAT: A mixin is a set of fields and methods that can be reused by multiple classes. +// WHY: To allow 'SmartSpeaker' to have connection logic without forcing 'SmartLight' to have it. mixin Connectable { + // WHAT: A boolean instance variable initialized to false. + // WHY: To track whether a specific device is currently linked to a network or bluetooth. bool isConnected = false; + + // WHAT: A function using arrow syntax (=>) to negate the current boolean. + // WHY: Provides a standardized, reusable way to flip the connection state for any class using this mixin. void toggleConnection() => isConnected = !isConnected; } +// WHAT: An abstract class acting as a base template. +// WHY: It defines the shared "DNA" of all devices but prevents anyone from creating a generic "SmartDevice". abstract class SmartDevice { + // WHAT: A final String that can only be set once. + // WHY: Ensures the device's unique identifier remains immutable (unchangeable) after creation. final String id; + + // WHAT: A private String (indicated by the underscore). + // WHY: Encapsulation; it hides raw data so we can control how it's accessed or formatted via getters/setters. String _name; + + // WHAT: Private boolean state for power. + // WHY: To keep the "ON/OFF" state internal to the object, requiring a method call to change it. bool _powerStatus = false; + + // WHAT: Private boolean for automation. + // WHY: To track if the device should handle its own power timing without user intervention. bool _auto_on_off = false; + // WHAT: A static constant String. + // WHY: This belongs to the class itself (the brand), not the individual instances. static const String brand = "GeminiHome"; - // 1. Standard Constructor with named parameters + // 1. Standard Constructor + // WHAT: A generative constructor using 'user_provided' naming. + // WHY: Maps the external input (user_provided_id) to the internal final field (id). SmartDevice({ - required this.id, - required String name - }) : _name = name; + required String user_provided_id, + required String user_provided_name + }) : id = user_provided_id, + _name = user_provided_name; - // 2. Named Constructor: Preset for Office use - SmartDevice.office({required this.id}) - : _name = "Office Desk Lamp", + // 2. Named Constructor: Office + // WHAT: A specialized constructor for a specific use-case. + // WHY: It streamlines creation by hardcoding the name and auto-mode for office settings. + SmartDevice.office({ + required String user_provided_id + }) : id = user_provided_id, + _name = "Office Desk Lamp", _auto_on_off = true; - // 3. Named Constructor: Preset for Eco mode - SmartDevice.eco({required this.id}) - : _name = "Eco Saver Unit", + // 3. Named Constructor: Eco + // WHAT: A specialized constructor for power saving. + // WHY: It ensures the device starts in an "OFF" state with "Auto" mode enabled for efficiency. + SmartDevice.eco({ + required String user_provided_id + }) : id = user_provided_id, + _name = "Eco Saver Unit", _powerStatus = false, _auto_on_off = true; - // 4. Factory Constructor using named parameters + // 4. Factory Constructor + // WHAT: A factory that decides which subclass to create. + // WHY: It allows the "SmartDevice" class to return a "SmartLight" or "SmartSpeaker" based on a String input. factory SmartDevice.create({ - required String type, - required String id, - required String name, + required String user_provided_type, + required String user_provided_id, + required String user_provided_name, }) { - if (type == 'light') return SmartLight(id: id, name: name); - return SmartSpeaker(id: id, name: name); - } - - // Specialized factory for the Office preset - factory SmartDevice.createOffice({required String id}) { - return SmartLight.office(id: id); + // WHAT: Logic to check the 'type' string. + // WHY: To determine which concrete implementation is needed for the user's request. + if (user_provided_type == 'light') { + return SmartLight(user_provided_id: user_provided_id, user_provided_name: user_provided_name); + } + return SmartSpeaker(user_provided_id: user_provided_id, user_provided_name: user_provided_name); } + // WHAT: A public Getter for the private name. + // WHY: Allows other parts of the app to read the name without being able to overwrite it directly. String get name => _name; - set name(String value) => _name = value.trim(); + // WHAT: A public Setter with a .trim() method. + // WHY: Cleans the input data (removing spaces) before saving it to the private '_name' field. + set name(String user_provided_new_name) => _name = user_provided_new_name.trim(); + + // WHAT: An abstract method signature (no curly braces). + // WHY: It forces every subclass to define what "performing an action" actually means for that specific device. void performAction(); + // WHAT: A concrete method shared by all subclasses. + // WHY: To avoid code duplication; every smart device uses the same logic to toggle power. void togglePower() { + // WHAT: Boolean inversion. + // WHY: Changes 'true' to 'false' or vice versa to simulate a power switch. _powerStatus = !_powerStatus; print('$_name is now ${_powerStatus ? "ON" : "OFF"} (Auto-mode: $_auto_on_off)'); } } +// WHAT: A concrete implementation of a SmartDevice. +// WHY: To add specific properties (like brightness) that only a light would have. class SmartLight extends SmartDevice { + // WHAT: An integer specific to this class. + // WHY: To track how bright the light is; initialized to 100 by default. int brightness = 100; - // Using 'super' syntax with named parameters + // WHAT: Standard constructor passing data to the super class. + // WHY: To ensure a custom light gets its ID and Name correctly initialized in the base class. SmartLight({ - required super.id, - required super.name - }); + required String user_provided_id, + required String user_provided_name + }) : super(user_provided_id: user_provided_id, user_provided_name: user_provided_name); - // Redirecting to parent named constructors - SmartLight.office({required String id}) : super.office(id: id); + // WHAT: Named constructor with an extra assignment in the initializer list. + // WHY: It combines the "Office" defaults from the parent with a specific "50%" brightness for this subclass. + SmartLight.office({ + required String user_provided_id + }) : brightness = 50, // Sets the brightness specifically for office use. + super.office(user_provided_id: user_provided_id); // Calls the parent's office logic. - SmartLight.eco({required String id}) : super.eco(id: id); + // WHAT: Named constructor for Eco mode. + // WHY: Redirects to the eco settings in the base class (e.g., auto-off enabled). + SmartLight.eco({ + required String user_provided_id + }) : brightness = 20, // WHY: Maybe Eco mode should be even dimmer to save more power! + super.eco(user_provided_id: user_provided_id); @override + // WHAT: Implementation of the abstract method. + // WHY: To display the specific state (brightness) of this light. void performAction() { print('[$_name] Brightness: $brightness%'); } } +// WHAT: A concrete implementation using inheritance (SmartDevice) and a mixin (Connectable). +// WHY: To create a device that has a name/id AND the ability to connect/disconnect. class SmartSpeaker extends SmartDevice with Connectable { + // WHAT: A double (0.0 to 1.0) for audio level. + // WHY: Specific data needed only for audio-based devices. double volume; + // WHAT: Constructor with an assertion check. + // WHY: The 'assert' stops the program during debugging if the volume is out of bounds (0.0 - 1.0). SmartSpeaker({ - required String id, - required String name, + required String user_provided_id, + required String user_provided_name, this.volume = 0.5, }) : assert(volume >= 0 && volume <= 1.0), - super(id: id, name: name); + super(user_provided_id: user_provided_id, user_provided_name: user_provided_name); @override + // WHAT: Concrete implementation of the abstract method. + // WHY: Defines that for a Speaker, "performing an action" includes checking the connection status. void performAction() { print('[$_name] Volume: ${(volume * 100).toInt()}%. Connected: $isConnected'); } } +// --- EXECUTION --- + void main() { - // --- USAGE --- - - // 1. Using the 'Office' preset with named ID - final workLamp = SmartLight.office(id: 'OFFICE_01'); + // WHAT: Instantiating a Light using the Office preset. + // WHY: To demonstrate how named constructors simplify creating specific device configurations. + final workLamp = SmartLight.office(user_provided_id: 'OFFICE_01'); - // 2. Using the 'Eco' preset with named ID - final ecoLight = SmartLight.eco(id: 'ECO_99'); + // WHAT: Instantiating a Light using the Eco preset. + // WHY: Shows the reuse of the super.eco logic. + final ecoLight = SmartLight.eco(user_provided_id: 'ECO_99'); - // 3. Standard creation using clear labels for ID and Name - final customLight = SmartLight(id: 'CUST_01', name: 'My Custom Light'); - - // 4. Using the factory with named parameters - final speaker = SmartDevice.create( - type: 'speaker', - id: 'SPK_01', - name: 'Living Room Bass' + // WHAT: Standard instantiation of a SmartLight. + // WHY: Demonstrates creating an object with custom labels. + final customLight = SmartLight( + user_provided_id: 'CUST_01', + user_provided_name: 'My Custom Light' ); + // WHAT: Using the factory constructor. + // WHY: Shows how the 'SmartDevice' base class can act as a single entry point to create different types of objects. + final speaker = SmartDevice.create( + user_provided_type: 'speaker', + user_provided_id: 'SPK_01', + user_provided_name: 'Living Room Bass' + ); + + // WHAT: Printing and calling methods. + // WHY: To verify that all constructors and methods are working as expected. print('--- Testing Presets ---'); workLamp.performAction(); workLamp.togglePower(); @@ -123,57 +201,6 @@ void main() { } -// You're absolutely right. Understanding *why* we use these features is more important than just knowing the syntax. Let’s break down the rationale and real-world use cases for the structure I built. - -// --- - -// ## 1. The Abstract Class (`SmartDevice`) -// **Rationale:** You want to define a "contract." Every smart device must have an ID, a name, and an action, but a "generic" smart device doesn't exist in reality—it’s always a specific light, speaker, or thermostat. - -// * **Use Case:** In a Large App, you might have a `List devices`. You can loop through this list and call `.togglePower()` on every item without knowing if it's a light or a speaker. This is the heart of **Polymorphism**. - -// --- - -// ## 2. Factory Constructors (`SmartDevice.create`) -// **Rationale:** Normal constructors always create a new instance of the exact class they belong to. A `factory` constructor is more flexible: it can return a subclass or even a cached instance. - -// * **Use Case:** **API Parsing.** When you get JSON data from a server, you don't know the device type until you read the data. A factory constructor can look at the "type" field in the JSON and decide to return a `SmartLight` object or a `SmartSpeaker` object automatically. - -// --- - -// ## 3. Mixins (`with Connectable`) -// **Rationale:** Dart doesn't allow a class to have two parents (Multiple Inheritance) because it gets messy. Mixins allow you to "plug in" shared behavior across unrelated classes. - - - -// * **Use Case:** Imagine you have a `SmartSpeaker` (a device) and a `PhoneApp` (not a device). Both need "WiFi Connection" logic. Instead of copying the code twice, you create a `Connectable` mixin and apply it to both. - -// --- - -// ## 4. Getters and Setters -// **Rationale:** This is about **Encapsulation**. You should never let outside code touch your internal variables (`_name`) directly. By using a setter, you can "sanitize" the data. - -// * **Use Case:** Validation. If a user tries to set a device name to a string full of empty spaces, your setter `set name(value) => _name = value.trim();` automatically cleans it up before it’s saved to your database. - -// --- - -// ## 5. Redirecting Constructors (`SmartSpeaker.loud`) -// **Rationale:** To avoid "Boilerplate" (repetitive code). If you have a common configuration for a class, you don't want to rewrite the initialization logic. - -// * **Use Case:** **Presets.** In a UI, you might have a "Default" button and a "Pro" button. Instead of manually setting 10 different parameters each time, you create a named constructor like `Button.primary()` that redirects to the main constructor with all the "Pro" settings pre-filled. - -// --- - -// ## 6. Static Members (`static const brand`) -// **Rationale:** Memory efficiency. If you have 1,000 smart lights, you don't need the string "GeminiHome" stored in memory 1,000 times. - -// * **Use Case:** **Configuration & Constants.** Use `static` for things that are "Universal truth" for the class, such as a maximum volume limit or a shared API version number. - -// Would you like me to show you how to handle **Interfaces** in Dart, which is the final piece of the OO puzzle? - - - -