I have a couple of d3 charts that share quite a lot of functionality. However they do diverge in a several areas. I'm considering moving them to separate classes based off a parent class because the singular buildChart function's length has grown unmaintainable.
The problem I'm facing right now is that even though it seems pretty easy to override certain methods from the parent in some instances, it would be nice to have the parent build most of one setup object, which contains properties like tick intervals, display formats, etc, but have each child class add a few properties to that object.
I thought to do this in the following way:
class Chart {
constructor({ svg, data, range }) {
this.svg = svg;
this.range = range;
this.data = data;
this.setDetails();
}
setDetails() {
this.details = {
sharedProp: "this property is shared"
};
}
scaffoldChart() {
/* initial d3 stuff */
}
}
export class SimilarChartUno extends Chart {
constructor({ svg, data, range }) {
super({ svg, data, range });
this.decorateDetails()
super.scaffoldChart()
}
decorateDetails() {
this.details = Object.assign(this.details, {
someUniqueProp: 'prop for UNO a'
})
}
}
// instance details :
{
"sharedProp": "this property is shared",
"someUniqueProp": "prop for UNO A"
}
This seems to work, but I have not seen an example like this anywhere: Is there is a better pattern than this?
Code Sandbox
There's nothing wrong with doing it the way you are currently, but if you'd like to avoid adding methods you could alter your SimilarChartUno to be something like this:
class SimilarChartUno extends Chart {
constructor({ svg, data, range }) {
super({ svg, data, range });
super.scaffoldChart()
}
setDetails() {
super.setDetails();
this.details.someUniqueProp = 'prop for UNO a';
}
}
In this way, you've maintained inheritance. The setDetails() is invoked in the super constructor with the child implementation. Alternatively, you can still perform an this.details = Object.assign(this.details, { complexObject: {} }); after the super.setDetails(); call for more complex assignments.
Related
I have a base class in ES6 like this:
class BasePlot {
props = {
'data': []
}
constructor() {
// do things but don't setup data
}
draw(data){
if (data && data.length )
this.props.data = data;
// Q: how to exit here if height and width are not yet available?
this.setScale()
this.setAxis()
}
setDimensions(height, width) {
this.props.height = height;
this.props.width = width;
}
}
The class will never be instantiated directly but will be used only for inheritance.
Apart the constructor, all the other methods might be called in unpredictable order, that is why in the draw method I don't want to proceed if height and width are not yet defined for the instance.
I could simply add an if condition and exit but that's not what I had in mind.
In a child class I call the parent draw like this:
class RectPlot extends BasePlot{
draw(data){
super.draw(data);
// DON'T EXECUTE if height and width are not set
// rest of the code
}
}
In this case when I call the child draw I first call the parent method, and in case the height and width are not set yet I'd like to exit (return) from the parent method but ALSO from the child one.
What I mean, is something like this:
// Parent
draw(data){
if (data && data.length )
this.props.data = data;
if(this.props.height && this.props.width)
this.setScale()
this.setAxis()
return true
}
else return false
}
}
// Child
draw(data){
if(super.draw(data)){
// proceed w rest of the code
}
else return false
}
This is exactly what I'd like to do, except I don't want to check with an if in all the subclasses if the parent draw completed successfully.
Q: Is there a way to 'early exit' a parent AND a child method besides the aforementioned repetition of the if-else block in all the children classes?
You cannot return from a caller method from within a called method. However, you could avoid the problem altogether...
An alternative design could be to create another method, say drawCanvas for example, as follows...
class BasePlot {
props = {
data: [],
};
draw(data) {
if (data && data.length) this.props.data = data;
if (this.props.height && this.props.width) this.drawCanvas(data);
}
drawCanvas(data) {
this.setScale();
this.setAxis();
}
}
class RectPlot extends BasePlot {
drawCanvas(data) {
super.drawCanvas(data);
// other stuff
}
}
You would then override drawCanvas instead of draw, knowing that drawCanvas is only called if width and height exist.
I agree that checking the returned value of super.draw(data), as in your example, is not ideal. It would be easy to forget this check, leading to potential errors. It is also not obvious to the reader of the child class implementation why this check is required without looking at the base class implementation.
This is a contrived example but it is similar to real-life situations where, for example, you might have a list of links built from data that you are AJAXing in from a server.
import {Component, e, render} from './node_modules/bd-core/lib.js';
// a list of strings that for alert when you click them
class AlertLinkList extends Component.withWatchables('data') {
handleClick(event){
alert(event.target.innerHTML);
}
bdElements() {
return e.div(
{
// bdReflect: ?????
},
['yes', 'no', 'maybe'].map(
txt => e.div({bdAdvise: {click: 'handleClick'}}, txt)
)
)
}
}
var linkList = render(AlertLinkList, {}, document.body);
// I would like to change the strings but this (obviously) does nothing
linkList.data = ['soup', 'nuts', 'fish', 'dessert'];
I can't think of a straightforward way to solve this.
bdReflect only works on writable DOM attributes, I think, so for example I could use it to replace the innerHTML of the component but then I think I lose the bdAdvise assignments on the links (and it also seems kinda kludgey).
Any ideas?
OK here's one pattern that works for this...
get rid of the watchables in AlertLinkList
instead, use kwargs to populate the list
wrap the list in another component that simply re-renders the list with new content whenever the content changes (e.g. after fetching new content from the server)
// a list of strings that alert when you click them
class AlertLinkList extends Component {
handleClick(event){
alert(event.target.innerHTML);
}
bdElements() {
return e.div(
this.kwargs.items.map(
txt => e.div({bdAdvise: {click: 'handleClick'}}, txt)
)
)
}
}
// a wrapper that provides/retrieves data for AlertLinkList
class LinkListWrapper extends Component {
bdElements() {
return e.div(
{},
e.a(
{bdAdvise: {click: 'updateList'}},
'Click to Update List',
),
e.div({bdAttach: 'listGoesHere'}),
);
}
updateList(event) {
// the data below would have been retrieved from the server...
const resultRetrievedFromServer = ['soup', 'nuts', 'fish', 'dessert'];
this.renderList(resultRetrievedFromServer)
}
renderList(items) {
render(AlertLinkList, {items}, this.listGoesHere, 'only')
}
postRender() {
const initialData = ['yes', 'no', 'maybe']
this.renderList(initialData);
}
}
var linkList = render(LinkListWrapper, {}, document.body);
The only issue I see here is that it may be suboptimal to re-render the entire wrapped component if only one small part of the data changed, though I suppose you could design around that.
Let's begin solving this problem by describing the public interface of AlertLinkList:
A component that contains a homogeneous list of children.
The state of each child is initialized by a pair of [text, url].
The list is mutated en masse.
Given this, your start is almost perfect. Here it is a again with a few minor modifications:
class AlertLinkList extends Component.withWatchables('data') {
handleClick(event) {
// do something when one of the children is clicked
}
bdElements() {
return e.div({}, this.data && this.data.map(item => e(AlertLink, { data: item })));
}
onMutateData(newValue) {
if (this.rendered) {
this.delChildren();
newValue && newValue.forEach(item => this.insChild(AlertLink, { data: item }));
}
}
}
See https://backdraftjs.org/tutorial.html#bd-tutorial.watchableProperties for an explanation of onMutateData.
Next we need to define the AlertLink component type; this is trivial:
class AlertLink extends Component {
bdElements() {
return e.a({
href: this.kwargs.data[1],
bdAdvise: { click: e => this.parent.handleClick(e) }
}, this.kwargs.data[0]);
}
}
The outline above will solve your problem. I've written the pen https://codepen.io/rcgill/pen/ExWrLbg to demonstrate.
You can also solve the problem with the backdraft Collection component https://backdraftjs.org/docs.html#bd-core.classes.Collection
I've written a pen https://codepen.io/rcgill/pen/WNpmeyx to demonstrate.
Lastly, if you're interested in writing the fewest lines of code possible and want a fairly immutable design, you don't have to factor out a child type. Here's a pen to demonstrate that: https://codepen.io/rcgill/pen/bGqZGgW
Which is best!?!? Well, it depends on your aims.
The first solution is simple and general and the children can be wrangled to do whatever you want them to do.
The second solution is very terse and includes a lot of additional capabilities not demonstrated. For example, with the backdraft Collection component
mutating the collection does not destroy/create new children, but rather alters the state of existing children. This is much more efficient and useful when implementing things like large grids.
you can mutate an individual elements in the collection
The third solution is very terse and very fixed. But sometimes that is all you need.
I have a class with a member variable of type object. This object has a fixed number of fields. I'm trying to decide if I should use one setter function or multiple to mutate these fields.
To make the question more concrete, I've written the following class to model a simple organizational management structure in two different ways:
Multiple Setter Functions
class Management {
constructor() {
this.numberOfManagers = 100;
this.salaryDetails = {
regionalManagerSalary: 80000,
stateManagerSalary: 110000,
executiveManagerSalary: 200000
};
}
setRegionalManagerSalary(salary) {
this.salaryDetails.regionalManagerSalary = salary;
}
setStateManagerSalary(salary) {
this.salaryDetails.stateManagerSalary = salary;
}
setExecutiveManagerSalary(salary) {
this.salaryDetails.executiveManagerSalary = salary;
}
}
const management = new Management();
management.setRegionalManagerSalary(100000);
management.setStateManagerSalary(120000);
management.setExecutiveManagerSalary(210000);
One Setter Function
class Management {
constructor() {
this.numberOfManagers = 100;
this.salaryDetails = {
regionalManagerSalary: 80000,
stateManagerSalary: 110000,
executiveManagerSalary: 200000
};
}
setManagerSalary(typeOfManagerSalary, salary) {
this.salaryDetails[typeOfManagerSalary] = salary;
}
}
const management = new Management();
management.setManagerSalary('regionalManagerSalary', 100000);
management.setManagerSalary('stateManagerSalary', 120000);
management.setManagerSalary('executiveManagerSalary', 210000);
Would implementation 1. be better or would implementation 2. be better?
I will always consider the higher readability approach if it doesn't trade off a lot of writability. The second one is more generic but the first one is more clear.
So I think the first one is better as it is more clear that there are only 3 fields in salaryDetails. Just consider you are not the author and when you see the second one, you have no idea how many fields there will be in salaryDetails as someone could just call management.setManagerSalary('someNewSalary', 100000); somewhere to add some new fields.
Unless you have more than one Management objects and each of them might have some specific fields under salaryDetails, I don't think the second approach is better.
My recommendation would be to have one method say setSalaryDetails which will take salary object as input which you can set directly. Something like -
setSalaryDetails(salaryDetails){this.salaryDetails = salaryDetails; }
When you invoke this method you can appropriately create this object with those fields set that you will pass to it.
It is better to not have any setter at all. Initialize all the required properties in the constructor (let the constructor require arguments for this).
The code you posted it not OOP but procedural code under disguise. If you want to make it OOP then the code must stay where the data is, i.e. in the class.
As a first improvement step, your code should look like this:
class Management {
constructor(nb, regionalSalary, stateSalary, executiveSalary) {
this.numberOfManagers = nb;
this.salaryDetails = {
regionalManagerSalary: regionalSalary,
stateManagerSalary: stateSalary,
executiveManagerSalary: executiveSalary
};
}
}
const management = new Management(100, 100000, 120000, 210000);
Shorter, much cleaner, less error prone.
The fragment you posted is not enough to tell what you want to achieve but the new signature of the constructor tells me that you are mixing too many responsibilities into a single class.
A cleaner design is to use separate classes for each manager type:
/* abstract */ class Manager {
constructor(salary) {
this.salary = salary;
}
}
class RegionalManager extends Manager { }
class StateManager extends Manager { }
class ExecutiveManager extends Manager { }
class Management {
constructor(nb, regionalManager, stateManger, executiveManager) {
this.numberOfManagers = nb;
this.managers = {
regionalManager: regionaManager,
stateManager: stateManager,
executiveManager: executiveManager
};
}
}
const management = new Management(
100,
new RegionalManager(100000),
new StateManager(120000),
new ExecutiveManager(210000)
);
You can now implement the logic common to all manager types in class Manager, the logic that is specific to each manager type in its class and the logic that handles the managers by number, without caring about their individual characteristics, in class Management. This way the code uses less if and switch conditions, is easier to read, understand and develop further.
Reactive View components (React, Angular, Vue, etc) revolutionized interface programming by eliminating the need to keep the view up to date with props/state. But as far as I know an analogous Model pattern has not been implemented/popularized (if Redux solves this problem, it's unclear to me how it can do so with classes).
The main area I'm running into a need for this (across many applications) is when some kind of constraining is involved. Some child/related model needs to be constrained when it is created, when it is updated, when the parent is updated, or when siblings are updated.
It could be done something like the following (just threw this together quickly to illustrate), but this isn't very efficient at scale because it's not selective (i.e. React only rerenders components that change).
Are there any implementations of this kind of data structure, or is this something I have to roll myself?
class ReactiveModel {
setState(newState) {
this.state = newState;
this.updateChildren();
}
setProps(newProps) {
this.props = newProps;
this.updateChildren();
}
updateChildren() {
var childrenSpecification = this.renderChildren();
//for each child
//create a new instance if one doesn't exist
//update props if any need to be updated
//updating props or state on a child triggers updateChildren on it so updating bubbles down
}
}
class Box extends ReactiveModel {
constructor(props) {
super(props);
this.state = {
boxObjects: this.props.boxObjectsFromApi //just plain data - "Object" instances, not "Box" instances
}
}
addBox(boxObject) {
this.setState({boxObjects: [...this.state.boxObjects, boxObject]}); //should call updateChildren
}
renderChildren() {
return {
boxes: this.state.boxObjects.map(({width}) => {
return {
class: Box,
props: {
width: Math.min(this.props.width, width) //constrain child width to be inside parent width
}
};
}
}
}
}
I know it's a pretty basic question, but still haven't found answers in half an hour of research.
I have two classes. One holds my Google Maps API. And when a marker is clicked I want to emit event on the other class and pass it the data.
I have written some pseudo code, so you can imagine of what I want to do.
export class Class1 {
someFunctionInClass1() {
//some code
this.functionHere()
}
functionHere() {
gotanEvent() {
google.maps.event {
//here i got an event
and want to pass data to Class2
functionToCallWhenEventEmits(with the data)
}
}
}
}
import { Class1 } from ../class1Path;
export class Class2 {
constructor(public class1: Class1) {
}
someFunctionInClass2() {
class1.someFunctionInClass1();
}
functionToCallWhenEventEmits(data to pass) {
//do something
}
}
Hope, someone can help me. :)
If you want event-like behavior between two classes, you can use a lightweight version of the Observer design pattern. Instead of maintaining a "list of its dependents", your subject will instead maintain only one dependent.
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems. (Wikipedia)
Here is an example of a lightweight observer pattern (with a Fiddle).
class Subject
{
private observer : Observer;
public Attach(observer: Observer)
{
this.observer = observer;
}
public DoSomething(message: string)
{
if (this.observer != null)
{
observer.Updating(message);
}
}
}
class Observer
{
public Updating(message: string)
{
document.writeln(message);
}
}
var subject = new Subject();
var observer = new Observer();
subject.Attach(observer);
subject.DoSomething("Something happened!");